diff --git a/easy_profiler_core/include/easy/reader.h b/easy_profiler_core/include/easy/reader.h index 50a14f4..0a1687c 100644 --- a/easy_profiler_core/include/easy/reader.h +++ b/easy_profiler_core/include/easy/reader.h @@ -45,32 +45,36 @@ The Apache License, Version 2.0 (the "License"); ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include + #include #include #include -#include -#include -#include -#include ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// namespace profiler { + using processid_t = uint64_t; using calls_number_t = uint32_t; using block_index_t = uint32_t; #pragma pack(push, 1) struct BlockStatistics EASY_FINAL { - ::profiler::timestamp_t total_duration; ///< Total duration of all block calls - ::profiler::timestamp_t total_children_duration; ///< Total duration of all children of all block calls - ::profiler::block_index_t min_duration_block; ///< Will be used in GUI to jump to the block with min duration - ::profiler::block_index_t max_duration_block; ///< Will be used in GUI to jump to the block with max duration - ::profiler::block_index_t parent_block; ///< Index of block which is "parent" for "per_parent_stats" or "frame" for "per_frame_stats" or thread-id for "per_thread_stats" - ::profiler::calls_number_t calls_number; ///< Block calls number + profiler::timestamp_t total_duration; ///< Total duration of all block calls + profiler::timestamp_t total_children_duration; ///< Total duration of all children of all block calls + profiler::block_index_t min_duration_block; ///< Will be used in GUI to jump to the block with min duration + profiler::block_index_t max_duration_block; ///< Will be used in GUI to jump to the block with max duration + profiler::block_index_t parent_block; ///< Index of block which is "parent" for "per_parent_stats" or "frame" for "per_frame_stats" or thread-id for "per_thread_stats" + profiler::calls_number_t calls_number; ///< Block calls number - explicit BlockStatistics(::profiler::timestamp_t _duration, ::profiler::block_index_t _block_index, ::profiler::block_index_t _parent_index) + explicit BlockStatistics(profiler::timestamp_t _duration, profiler::block_index_t _block_index, profiler::block_index_t _parent_index) : total_duration(_duration) , total_children_duration(0) , min_duration_block(_block_index) @@ -82,7 +86,7 @@ namespace profiler { //BlockStatistics() = default; - inline ::profiler::timestamp_t average_duration() const + inline profiler::timestamp_t average_duration() const { return total_duration / calls_number; } @@ -100,20 +104,20 @@ namespace profiler { public: - using blocks_t = ::std::vector; - using children_t = ::std::vector<::profiler::block_index_t>; + using blocks_t = std::vector; + using children_t = std::vector; children_t children; ///< List of children blocks. May be empty. union { - ::profiler::SerializedBlock* node; ///< Pointer to serialized data for regular block (id, name, begin, end etc.) - ::profiler::SerializedCSwitch* cs; ///< Pointer to serialized data for context switch (thread_id, name, begin, end etc.) - ::profiler::ArbitraryValue* value; ///< Pointer to serialized data for arbitrary value + profiler::SerializedBlock* node; ///< Pointer to serialized data for regular block (id, name, begin, end etc.) + profiler::SerializedCSwitch* cs; ///< Pointer to serialized data for context switch (thread_id, name, begin, end etc.) + profiler::ArbitraryValue* value; ///< Pointer to serialized data for arbitrary value }; - ::profiler::BlockStatistics* per_parent_stats; ///< Pointer to statistics for this block within the parent (may be nullptr for top-level blocks) - ::profiler::BlockStatistics* per_frame_stats; ///< Pointer to statistics for this block within the frame (may be nullptr for top-level blocks) - ::profiler::BlockStatistics* per_thread_stats; ///< Pointer to statistics for this block within the bounds of all frames per current thread + profiler::BlockStatistics* per_parent_stats; ///< Pointer to statistics for this block within the parent (may be nullptr for top-level blocks) + profiler::BlockStatistics* per_frame_stats; ///< Pointer to statistics for this block within the frame (may be nullptr for top-level blocks) + profiler::BlockStatistics* per_thread_stats; ///< Pointer to statistics for this block within the bounds of all frames per current thread uint8_t depth; ///< Maximum number of sublevels (maximum children depth) BlocksTree(const This&) = delete; @@ -132,12 +136,12 @@ namespace profiler { BlocksTree(This&& that) EASY_NOEXCEPT : BlocksTree() { - make_move(::std::forward(that)); + make_move(std::forward(that)); } This& operator = (This&& that) EASY_NOEXCEPT { - make_move(::std::forward(that)); + make_move(std::forward(that)); return *this; } @@ -166,7 +170,7 @@ namespace profiler { // shrink version 2: //children_t new_children; //new_children.reserve(children.size()); - //::std::move(children.begin(), children.end(), ::std::back_inserter(new_children)); + //std::move(children.begin(), children.end(), std::back_inserter(new_children)); //new_children.swap(children); } @@ -183,7 +187,7 @@ namespace profiler { if (per_frame_stats != that.per_frame_stats) release_stats(per_frame_stats); - children = ::std::move(that.children); + children = std::move(that.children); node = that.node; per_parent_stats = that.per_parent_stats; per_frame_stats = that.per_frame_stats; @@ -210,11 +214,11 @@ namespace profiler { BlocksTree::children_t sync; ///< List of context-switch events BlocksTree::children_t events; ///< List of events indexes std::string thread_name; ///< Name of this thread - ::profiler::timestamp_t profiled_time; ///< Profiled time of this thread (sum of all children duration) - ::profiler::timestamp_t wait_time; ///< Wait time of this thread (sum of all context switches) - ::profiler::thread_id_t thread_id; ///< System Id of this thread - ::profiler::block_index_t frames_number; ///< Total frames number (top-level blocks) - ::profiler::block_index_t blocks_number; ///< Total blocks number including their children + profiler::timestamp_t profiled_time; ///< Profiled time of this thread (sum of all children duration) + profiler::timestamp_t wait_time; ///< Wait time of this thread (sum of all context switches) + profiler::thread_id_t thread_id; ///< System Id of this thread + profiler::block_index_t frames_number; ///< Total frames number (top-level blocks) + profiler::block_index_t blocks_number; ///< Total blocks number including their children uint8_t depth; ///< Maximum stack depth (number of levels) BlocksTreeRoot(const This&) = delete; @@ -226,10 +230,10 @@ namespace profiler { } BlocksTreeRoot(This&& that) EASY_NOEXCEPT - : children(::std::move(that.children)) - , sync(::std::move(that.sync)) - , events(::std::move(that.events)) - , thread_name(::std::move(that.thread_name)) + : children(std::move(that.children)) + , sync(std::move(that.sync)) + , events(std::move(that.events)) + , thread_name(std::move(that.thread_name)) , profiled_time(that.profiled_time) , wait_time(that.wait_time) , thread_id(that.thread_id) @@ -241,10 +245,10 @@ namespace profiler { This& operator = (This&& that) EASY_NOEXCEPT { - children = ::std::move(that.children); - sync = ::std::move(that.sync); - events = ::std::move(that.events); - thread_name = ::std::move(that.thread_name); + children = std::move(that.children); + sync = std::move(that.sync); + events = std::move(that.events); + thread_name = std::move(that.thread_name); profiled_time = that.profiled_time; wait_time = that.wait_time; thread_id = that.thread_id; @@ -271,8 +275,9 @@ namespace profiler { }; // END of class BlocksTreeRoot. - using blocks_t = ::profiler::BlocksTree::blocks_t; - using thread_blocks_tree_t = ::std::unordered_map<::profiler::thread_id_t, ::profiler::BlocksTreeRoot, ::estd::hash<::profiler::thread_id_t> >; + using blocks_t = profiler::BlocksTree::blocks_t; + using thread_blocks_tree_t = std::unordered_map >; + using block_getter_fn = std::function; ////////////////////////////////////////////////////////////////////////// @@ -367,59 +372,113 @@ namespace profiler { ////////////////////////////////////////////////////////////////////////// - using descriptors_list_t = ::std::vector; + using descriptors_list_t = std::vector; } // 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, + 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, + uint32_t& total_descriptors_number, + uint32_t& version, + profiler::processid_t& pid, + bool gather_statistics, + std::ostream& _log); + + PROFILER_API profiler::block_index_t fillTreesFromStream(std::atomic& progress, std::istream& 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, uint32_t& total_descriptors_number, uint32_t& version, + profiler::processid_t& pid, bool gather_statistics, - ::std::stringstream& _log); + std::ostream& _log); - PROFILER_API ::profiler::block_index_t fillTreesFromStream(::std::atomic& progress, ::std::stringstream& str, - ::profiler::SerializedData& serialized_blocks, - ::profiler::SerializedData& serialized_descriptors, - ::profiler::descriptors_list_t& descriptors, - ::profiler::blocks_t& _blocks, - ::profiler::thread_blocks_tree_t& threaded_trees, - uint32_t& total_descriptors_number, - uint32_t& version, - bool gather_statistics, - ::std::stringstream& _log); + PROFILER_API bool readDescriptionsFromStream(std::atomic& progress, std::istream& str, + profiler::SerializedData& serialized_descriptors, + profiler::descriptors_list_t& descriptors, + std::ostream& _log); - PROFILER_API bool readDescriptionsFromStream(::std::atomic& progress, ::std::stringstream& str, - ::profiler::SerializedData& serialized_descriptors, - ::profiler::descriptors_list_t& descriptors, - ::std::stringstream& _log); + PROFILER_API profiler::block_index_t writeTreesToFile(std::atomic& progress, const char* filename, + const profiler::SerializedData& serialized_descriptors, + profiler::block_id_t descriptors_count, + const profiler::thread_blocks_tree_t& trees, + profiler::block_getter_fn block_getter, + profiler::timestamp_t begin_time, + profiler::timestamp_t end_time, + profiler::processid_t pid, + std::ostream& log); + + PROFILER_API profiler::block_index_t writeTreesToStream(std::atomic& progress, std::ostream& str, + const profiler::SerializedData& serialized_descriptors, + profiler::block_id_t descriptors_count, + const profiler::thread_blocks_tree_t& trees, + profiler::block_getter_fn block_getter, + profiler::timestamp_t begin_time, + profiler::timestamp_t end_time, + profiler::processid_t pid, + std::ostream& 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, - uint32_t& total_descriptors_number, - uint32_t& version, - bool gather_statistics, - ::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, + uint32_t& total_descriptors_number, + uint32_t& version, + profiler::processid_t& pid, + bool gather_statistics, + std::ostream& _log) { - ::std::atomic progress = ATOMIC_VAR_INIT(0); - return fillTreesFromFile(progress, filename, serialized_blocks, serialized_descriptors, descriptors, _blocks, threaded_trees, total_descriptors_number, version, gather_statistics, _log); + std::atomic progress = ATOMIC_VAR_INIT(0); + return fillTreesFromFile(progress, filename, serialized_blocks, serialized_descriptors, descriptors, _blocks, + threaded_trees, total_descriptors_number, version, pid, gather_statistics, _log); } -inline bool readDescriptionsFromStream(::std::stringstream& str, - ::profiler::SerializedData& serialized_descriptors, - ::profiler::descriptors_list_t& descriptors, - ::std::stringstream& _log) +inline profiler::block_index_t writeTreesToFile(const char* filename, + const profiler::SerializedData& serialized_descriptors, + profiler::block_id_t descriptors_count, + const profiler::thread_blocks_tree_t& trees, + profiler::block_getter_fn block_getter, + profiler::timestamp_t begin_time, + profiler::timestamp_t end_time, + profiler::processid_t pid, + std::ostream& log) { - ::std::atomic progress = ATOMIC_VAR_INIT(0); + std::atomic progress = ATOMIC_VAR_INIT(0); + return writeTreesToFile(progress, filename, serialized_descriptors, descriptors_count, trees, std::move(block_getter), + begin_time, end_time, pid, log); +} + +inline profiler::block_index_t writeTreesToStream(std::ostream& str, + const profiler::SerializedData& serialized_descriptors, + profiler::block_id_t descriptors_count, + const profiler::thread_blocks_tree_t& trees, + profiler::block_getter_fn block_getter, + profiler::timestamp_t begin_time, + profiler::timestamp_t end_time, + profiler::processid_t pid, + std::ostream& log) +{ + std::atomic progress = ATOMIC_VAR_INIT(0); + return writeTreesToStream(progress, str, serialized_descriptors, descriptors_count, trees, std::move(block_getter), + begin_time, end_time, pid, log); +} + +inline bool readDescriptionsFromStream(std::istream& str, + profiler::SerializedData& serialized_descriptors, + profiler::descriptors_list_t& descriptors, + std::ostream& _log) +{ + std::atomic progress = ATOMIC_VAR_INIT(0); return readDescriptionsFromStream(progress, str, serialized_descriptors, descriptors, _log); } diff --git a/easy_profiler_core/profile_manager.h b/easy_profiler_core/profile_manager.h index c0a0cc0..5baf625 100644 --- a/easy_profiler_core/profile_manager.h +++ b/easy_profiler_core/profile_manager.h @@ -65,7 +65,7 @@ The Apache License, Version 2.0 (the "License"); ////////////////////////////////////////////////////////////////////////// -typedef uint64_t processid_t; +using processid_t = uint64_t; class BlockDescriptor; @@ -79,14 +79,15 @@ class ProfileManager ProfileManager(); - typedef profiler::guard_lock guard_lock_t; - typedef std::map map_of_threads_stacks; - typedef std::vector block_descriptors_t; + using atomic_timestamp_t = std::atomic; + using guard_lock_t = profiler::guard_lock; + using map_of_threads_stacks = std::map; + using block_descriptors_t = std::vector; #ifdef EASY_PROFILER_HASHED_CSTR_DEFINED - typedef std::unordered_map descriptors_map_t; + using descriptors_map_t = std::unordered_map; #else - typedef std::unordered_map descriptors_map_t; + using descriptors_map_t = std::unordered_map; #endif const processid_t m_processId; @@ -97,9 +98,9 @@ class ProfileManager uint64_t m_descriptorsMemorySize; profiler::timestamp_t m_beginTime; profiler::timestamp_t m_endTime; - std::atomic m_frameMax; - std::atomic m_frameAvg; - std::atomic m_frameCur; + atomic_timestamp_t m_frameMax; + atomic_timestamp_t m_frameAvg; + atomic_timestamp_t m_frameCur; profiler::spin_lock m_spin; profiler::spin_lock m_storedSpin; profiler::spin_lock m_dumpSpin; diff --git a/easy_profiler_core/reader.cpp b/easy_profiler_core/reader.cpp index 13a7ff5..a865253 100644 --- a/easy_profiler_core/reader.cpp +++ b/easy_profiler_core/reader.cpp @@ -71,9 +71,7 @@ #include "hashed_cstr.h" -#include #include -#include #include #include #include @@ -81,8 +79,6 @@ ////////////////////////////////////////////////////////////////////////// -typedef uint64_t processid_t; - extern const uint32_t PROFILER_SIGNATURE; extern const uint32_t EASY_CURRENT_VERSION; @@ -123,20 +119,35 @@ const uint64_t TIME_FACTOR = 1000000000ULL; ////////////////////////////////////////////////////////////////////////// +struct BlocksRange +{ + profiler::block_index_t begin; + profiler::block_index_t end; +}; + +struct BlocksAndCSwitchesRange +{ + BlocksRange blocks; + BlocksRange cswitches; +}; + +////////////////////////////////////////////////////////////////////////// + inline bool isCompatibleVersion(uint32_t _version) { return _version >= MIN_COMPATIBLE_VERSION; } -inline void write(::std::stringstream& _stream, const char* _value, size_t _size) +template +inline void write(std::ostream& _stream, const char* _data, T _size) { - _stream.write(_value, _size); + _stream.write(_data, _size); } template -inline void write(::std::stringstream& _stream, const T& _value) +inline void write(std::ostream& _stream, const T& _data) { - _stream.write((const char*)&_value, sizeof(T)); + _stream.write((const char*)&_data, sizeof(T)); } ////////////////////////////////////////////////////////////////////////// @@ -189,20 +200,20 @@ namespace profiler { #ifdef EASY_PROFILER_HASHED_CSTR_DEFINED -using StatsMap = ::std::unordered_map<::profiler::block_id_t, ::profiler::BlockStatistics*, ::estd::hash<::profiler::block_id_t> >; +using StatsMap = std::unordered_map >; /** \note It is absolutely safe to use hashed_cstr (which simply stores pointer) because std::unordered_map, which uses it as a key, exists only inside fillTreesFromFile function. */ -using IdMap = ::std::unordered_map<::profiler::hashed_cstr, ::profiler::block_id_t>; +using IdMap = std::unordered_map; -using CsStatsMap = ::std::unordered_map<::profiler::hashed_cstr, ::profiler::BlockStatistics*>; +using CsStatsMap = std::unordered_map; #else // TODO: Create optimized version of profiler::hashed_cstr for Linux too. -using StatsMap = ::std::unordered_map<::profiler::block_id_t, ::profiler::BlockStatistics*, ::estd::hash<::profiler::block_id_t> >; -using IdMap = ::std::unordered_map<::profiler::hashed_stdstring, ::profiler::block_id_t>; -using CsStatsMap = ::std::unordered_map<::profiler::hashed_stdstring, ::profiler::BlockStatistics*>; +using StatsMap = std::unordered_map >; +using IdMap = std::unordered_map; +using CsStatsMap = std::unordered_map; #endif @@ -220,7 +231,7 @@ using CsStatsMap = ::std::unordered_map<::profiler::hashed_stdstring, ::profiler automatically receive statistics update. */ -static ::profiler::BlockStatistics* update_statistics(StatsMap& _stats_map, const ::profiler::BlocksTree& _current, ::profiler::block_index_t _current_index, ::profiler::block_index_t _parent_index, const ::profiler::blocks_t& _blocks, bool _calculate_children = true) +static profiler::BlockStatistics* update_statistics(StatsMap& _stats_map, const profiler::BlocksTree& _current, profiler::block_index_t _current_index, profiler::block_index_t _parent_index, const profiler::blocks_t& _blocks, bool _calculate_children = true) { auto duration = _current.node->duration(); //StatsMap::key_type key(_current.node->name()); @@ -262,7 +273,7 @@ static ::profiler::BlockStatistics* update_statistics(StatsMap& _stats_map, cons // This is first time the block appear in the file. // Create new statistics. - auto stats = new ::profiler::BlockStatistics(duration, _current_index, _parent_index); + auto stats = new profiler::BlockStatistics(duration, _current_index, _parent_index); //_stats_map.emplace(key, stats); _stats_map.emplace(_current.node->id(), stats); @@ -275,7 +286,7 @@ static ::profiler::BlockStatistics* update_statistics(StatsMap& _stats_map, cons return stats; } -static ::profiler::BlockStatistics* update_statistics(CsStatsMap& _stats_map, const ::profiler::BlocksTree& _current, ::profiler::block_index_t _current_index, ::profiler::block_index_t _parent_index, const ::profiler::blocks_t& _blocks, bool _calculate_children = true) +static profiler::BlockStatistics* update_statistics(CsStatsMap& _stats_map, const profiler::BlocksTree& _current, profiler::block_index_t _current_index, profiler::block_index_t _parent_index, const profiler::blocks_t& _blocks, bool _calculate_children = true) { auto duration = _current.node->duration(); CsStatsMap::key_type key(_current.node->name()); @@ -316,7 +327,7 @@ static ::profiler::BlockStatistics* update_statistics(CsStatsMap& _stats_map, co // This is first time the block appear in the file. // Create new statistics. - auto stats = new ::profiler::BlockStatistics(duration, _current_index, _parent_index); + auto stats = new profiler::BlockStatistics(duration, _current_index, _parent_index); _stats_map.emplace(key, stats); if (_calculate_children) @@ -330,7 +341,7 @@ static ::profiler::BlockStatistics* update_statistics(CsStatsMap& _stats_map, co ////////////////////////////////////////////////////////////////////////// -static void update_statistics_recursive(StatsMap& _stats_map, ::profiler::BlocksTree& _current, ::profiler::block_index_t _current_index, ::profiler::block_index_t _parent_index, ::profiler::blocks_t& _blocks) +static void update_statistics_recursive(StatsMap& _stats_map, profiler::BlocksTree& _current, profiler::block_index_t _current_index, profiler::block_index_t _parent_index, profiler::blocks_t& _blocks) { _current.per_frame_stats = update_statistics(_stats_map, _current, _current_index, _parent_index, _blocks, false); for (auto i : _current.children) @@ -342,44 +353,9 @@ static void update_statistics_recursive(StatsMap& _stats_map, ::profiler::Blocks ////////////////////////////////////////////////////////////////////////// -/*void validate_pointers(::std::atomic& _progress, const char* _oldbase, ::profiler::SerializedData& _serialized_blocks, ::profiler::blocks_t& _blocks, size_t _size) +static bool update_progress(std::atomic& progress, int new_value, std::ostream& _log) { - if (_oldbase == nullptr) - { - _progress.store(25, ::std::memory_order_release); - return; - } - - for (size_t i = 0; i < _size; ++i) - { - auto& tree = _blocks[i]; - auto dist = ::std::distance(_oldbase, reinterpret_cast(tree.node)); - tree.node = reinterpret_cast<::profiler::SerializedBlock*>(_serialized_blocks.data() + dist); - _progress.store(20 + static_cast(5 * i / _size), ::std::memory_order_release); - } -} - -void validate_pointers(::std::atomic& _progress, const char* _oldbase, ::profiler::SerializedData& _serialized_descriptors, ::profiler::descriptors_list_t& _descriptors, size_t _size) -{ - if (_oldbase == nullptr) - { - _progress.store(5, ::std::memory_order_release); - return; - } - - for (size_t i = 0; i < _size; ++i) - { - auto dist = ::std::distance(_oldbase, reinterpret_cast(_descriptors[i])); - _descriptors[i] = reinterpret_cast<::profiler::SerializedBlockDescriptor*>(_serialized_descriptors.data() + dist); - _progress.store(static_cast(5 * i / _size)); - } -}*/ - -////////////////////////////////////////////////////////////////////////// - -static bool update_progress(::std::atomic& progress, int new_value, ::std::stringstream& _log) -{ - auto oldprogress = progress.exchange(new_value, ::std::memory_order_release); + auto oldprogress = progress.exchange(new_value, std::memory_order_release); if (oldprogress < 0) { _log << "Reading was interrupted"; @@ -389,13 +365,25 @@ static bool update_progress(::std::atomic& progress, int new_value, ::std:: return true; } +static bool update_progress_write(std::atomic& progress, int new_value, std::ostream& _log) +{ + auto oldprogress = progress.exchange(new_value, std::memory_order_release); + if (oldprogress < 0) + { + _log << "Writing was interrupted"; + return false; + } + + return true; +} + ////////////////////////////////////////////////////////////////////////// struct EasyFileHeader { uint32_t signature = 0; uint32_t version = 0; - processid_t pid = 0; + profiler::processid_t pid = 0; int64_t cpu_frequency = 0; profiler::timestamp_t begin_time = 0; profiler::timestamp_t end_time = 0; @@ -405,7 +393,7 @@ struct EasyFileHeader uint32_t total_descriptors_number = 0; }; -bool readHeader_v1(EasyFileHeader& _header, ::std::stringstream& inFile, std::stringstream& _log) +bool readHeader_v1(EasyFileHeader& _header, std::istream& inFile, std::ostream& _log) { // File header before v2.0.0 @@ -419,13 +407,13 @@ bool readHeader_v1(EasyFileHeader& _header, ::std::stringstream& inFile, std::st } else { - inFile.read((char*)&_header.pid, sizeof(processid_t)); + inFile.read((char*)&_header.pid, sizeof(decltype(_header.pid))); } } inFile.read((char*)&_header.cpu_frequency, sizeof(int64_t)); - inFile.read((char*)&_header.begin_time, sizeof(::profiler::timestamp_t)); - inFile.read((char*)&_header.end_time, sizeof(::profiler::timestamp_t)); + inFile.read((char*)&_header.begin_time, sizeof(profiler::timestamp_t)); + inFile.read((char*)&_header.end_time, sizeof(profiler::timestamp_t)); inFile.read((char*)&_header.total_blocks_number, sizeof(uint32_t)); if (_header.total_blocks_number == 0) @@ -458,14 +446,14 @@ bool readHeader_v1(EasyFileHeader& _header, ::std::stringstream& inFile, std::st return true; } -bool readHeader_v2(EasyFileHeader& _header, ::std::stringstream& inFile, std::stringstream& _log) +bool readHeader_v2(EasyFileHeader& _header, std::istream& inFile, std::ostream& _log) { // File header after v2.0.0 - inFile.read((char*)&_header.pid, sizeof(processid_t)); + inFile.read((char*)&_header.pid, sizeof(decltype(_header.pid))); inFile.read((char*)&_header.cpu_frequency, sizeof(int64_t)); - inFile.read((char*)&_header.begin_time, sizeof(::profiler::timestamp_t)); - inFile.read((char*)&_header.end_time, sizeof(::profiler::timestamp_t)); + inFile.read((char*)&_header.begin_time, sizeof(profiler::timestamp_t)); + inFile.read((char*)&_header.end_time, sizeof(profiler::timestamp_t)); inFile.read((char*)&_header.memory_size, sizeof(decltype(_header.memory_size))); if (_header.memory_size == 0) @@ -502,60 +490,52 @@ bool readHeader_v2(EasyFileHeader& _header, ::std::stringstream& inFile, std::st 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, - uint32_t& total_descriptors_number, - uint32_t& version, - bool gather_statistics, - ::std::stringstream& _log) + 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, + uint32_t& total_descriptors_number, + uint32_t& version, + profiler::processid_t& pid, + bool gather_statistics, + std::ostream& _log) { if (!update_progress(progress, 0, _log)) { return 0; } - ::std::ifstream inFile(filename, ::std::fstream::binary); + std::ifstream inFile(filename, std::fstream::binary); if (!inFile.is_open()) { _log << "Can not open file " << filename; return 0; } - ::std::stringstream str; - - // Replace str buffer to inFile buffer to avoid redundant copying - typedef ::std::basic_iostream<::std::stringstream::char_type, ::std::stringstream::traits_type> stringstream_parent; - stringstream_parent& s = str; - auto oldbuf = s.rdbuf(inFile.rdbuf()); - // Read data from file - auto result = fillTreesFromStream(progress, str, serialized_blocks, serialized_descriptors, descriptors, blocks, - threaded_trees, total_descriptors_number, version, gather_statistics, _log); - - // Restore old str buffer to avoid possible second memory free on stringstream destructor - s.rdbuf(oldbuf); + auto result = fillTreesFromStream(progress, inFile, serialized_blocks, serialized_descriptors, descriptors, blocks, + threaded_trees, total_descriptors_number, version, pid, gather_statistics, _log); return result; } ////////////////////////////////////////////////////////////////////////// - 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, - uint32_t& total_descriptors_number, - uint32_t& version, - bool gather_statistics, - ::std::stringstream& _log) + PROFILER_API profiler::block_index_t fillTreesFromStream(std::atomic& progress, std::istream& 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, + uint32_t& total_descriptors_number, + uint32_t& version, + profiler::processid_t& pid, + bool gather_statistics, + std::ostream& _log) { - EASY_FUNCTION(::profiler::colors::Cyan); + EASY_FUNCTION(profiler::colors::Cyan); if (!update_progress(progress, 0, _log)) { @@ -593,6 +573,8 @@ extern "C" { return 0; } + pid = header.pid; + const uint64_t cpu_frequency = header.cpu_frequency; const double conversion_factor = (cpu_frequency != 0 ? static_cast(TIME_FACTOR) / static_cast(cpu_frequency) : 1.); @@ -633,7 +615,7 @@ extern "C" { char* data = serialized_descriptors[i]; inFile.read(data, sz); - auto descriptor = reinterpret_cast<::profiler::SerializedBlockDescriptor*>(data); + auto descriptor = reinterpret_cast(data); descriptors.push_back(descriptor); i += sz; @@ -643,7 +625,7 @@ extern "C" { } } - using PerThreadStats = ::std::unordered_map<::profiler::thread_id_t, StatsMap, ::estd::hash<::profiler::thread_id_t> >; + using PerThreadStats = std::unordered_map >; PerThreadStats parent_statistics, frame_statistics; IdMap identification_table; @@ -654,16 +636,16 @@ extern "C" { i = 0; uint32_t read_number = 0; - ::profiler::block_index_t blocks_counter = 0; - ::std::vector name; + profiler::block_index_t blocks_counter = 0; + std::vector name; - const size_t thread_id_t_size = version < EASY_V_130 ? sizeof(uint32_t) : sizeof(::profiler::thread_id_t); + const size_t thread_id_t_size = version < EASY_V_130 ? sizeof(uint32_t) : sizeof(profiler::thread_id_t); while (!inFile.eof()) { - EASY_BLOCK("Read thread data", ::profiler::colors::DarkGreen); + EASY_BLOCK("Read thread data", profiler::colors::DarkGreen); - ::profiler::thread_id_t thread_id = 0; + profiler::thread_id_t thread_id = 0; inFile.read((char*)&thread_id, thread_id_t_size); if (inFile.eof()) break; @@ -686,7 +668,7 @@ extern "C" { auto threshold = read_number + blocks_number_in_thread; while (!inFile.eof() && read_number < threshold) { - EASY_BLOCK("Read context switch", ::profiler::colors::Green); + EASY_BLOCK("Read context switch", profiler::colors::Green); ++read_number; @@ -701,8 +683,8 @@ extern "C" { char* data = serialized_blocks[i]; inFile.read(data, sz); i += sz; - auto baseData = reinterpret_cast<::profiler::SerializedCSwitch*>(data); - auto t_begin = reinterpret_cast<::profiler::timestamp_t*>(data); + auto baseData = reinterpret_cast(data); + auto t_begin = reinterpret_cast(data); auto t_end = t_begin + 1; if (cpu_frequency != 0) @@ -717,7 +699,7 @@ extern "C" { *t_begin = begin_time; blocks.emplace_back(); - ::profiler::BlocksTree& tree = blocks.back(); + profiler::BlocksTree& tree = blocks.back(); tree.cs = baseData; const auto block_index = blocks_counter++; @@ -726,7 +708,7 @@ extern "C" { if (gather_statistics) { - EASY_BLOCK("Gather per thread statistics", ::profiler::colors::Coral); + EASY_BLOCK("Gather per thread statistics", profiler::colors::Coral); tree.per_thread_stats = update_statistics(per_thread_statistics_cs, tree, block_index, ~0U, blocks);//, thread_id, blocks); } } @@ -747,7 +729,7 @@ extern "C" { threshold = read_number + blocks_number_in_thread; while (!inFile.eof() && read_number < threshold) { - EASY_BLOCK("Read block", ::profiler::colors::Green); + EASY_BLOCK("Read block", profiler::colors::Green); ++read_number; @@ -762,7 +744,7 @@ extern "C" { char* data = serialized_blocks[i]; inFile.read(data, sz); i += sz; - auto baseData = reinterpret_cast<::profiler::SerializedBlock*>(data); + auto baseData = reinterpret_cast(data); if (baseData->id() >= total_descriptors_number) { _log << "Bad block id == " << baseData->id(); @@ -776,7 +758,7 @@ extern "C" { return 0; } - auto t_begin = reinterpret_cast<::profiler::timestamp_t*>(data); + auto t_begin = reinterpret_cast(data); auto t_end = t_begin + 1; if (cpu_frequency != 0) @@ -791,7 +773,7 @@ extern "C" { *t_begin = begin_time; blocks.emplace_back(); - ::profiler::BlocksTree& tree = blocks.back(); + profiler::BlocksTree& tree = blocks.back(); tree.node = baseData; const auto block_index = blocks_counter++; @@ -810,7 +792,7 @@ extern "C" { 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()); + auto id = static_cast(descriptors.size()); identification_table.emplace(key, id); if (descriptors.capacity() == descriptors.size()) descriptors.reserve((descriptors.size() * 3) >> 1); @@ -826,20 +808,20 @@ extern "C" { 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); + //auto lower = std::lower_bound(root.children.begin(), root.children.end(), tree); /**/ - EASY_BLOCK("Find children", ::profiler::colors::Blue); + 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)); + std::move(lower, root.children.end(), std::back_inserter(tree.children)); root.children.erase(lower, root.children.end()); EASY_END_BLOCK; if (gather_statistics) { - EASY_BLOCK("Gather statistic within parent", ::profiler::colors::Magenta); + EASY_BLOCK("Gather statistic within parent", profiler::colors::Magenta); auto& per_parent_statistics = parent_statistics[thread_id]; per_parent_statistics.clear(); @@ -883,14 +865,14 @@ extern "C" { } ++root.blocks_number; - root.children.emplace_back(block_index);// ::std::move(tree)); - if (desc->type() != ::profiler::BlockType::Block) + root.children.emplace_back(block_index);// std::move(tree)); + if (desc->type() != profiler::BlockType::Block) root.events.emplace_back(block_index); if (gather_statistics) { - EASY_BLOCK("Gather per thread statistics", ::profiler::colors::Coral); + EASY_BLOCK("Gather per thread statistics", profiler::colors::Coral); tree.per_thread_stats = update_statistics(per_thread_statistics, tree, block_index, ~0U, blocks);//, thread_id, blocks); } } @@ -907,10 +889,10 @@ extern "C" { return 0; // Loading interrupted } - EASY_BLOCK("Gather statistics for roots", ::profiler::colors::Purple); + EASY_BLOCK("Gather statistics for roots", profiler::colors::Purple); if (gather_statistics) { - ::std::vector<::std::thread> statistics_threads; + std::vector statistics_threads; statistics_threads.reserve(threaded_trees.size()); for (auto& it : threaded_trees) @@ -923,19 +905,19 @@ extern "C" { auto& per_parent_statistics = parent_statistics[it.first]; per_parent_statistics.clear(); - statistics_threads.emplace_back(::std::thread([&] (::profiler::BlocksTreeRoot& _root) + statistics_threads.emplace_back(std::thread([&] (profiler::BlocksTreeRoot& _root) { - //::std::sort(root.sync.begin(), root.sync.end(), [&blocks](::profiler::block_index_t left, ::profiler::block_index_t right) + //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(); //}); - ::profiler::block_index_t cs_index = 0; + profiler::block_index_t cs_index = 0; for (auto child_index : _root.children) { auto& frame = blocks[child_index]; - if (descriptors[frame.node->id()]->type() == ::profiler::BlockType::Block) + if (descriptors[frame.node->id()]->type() == profiler::BlockType::Block) ++_root.frames_number; frame.per_parent_stats = update_statistics(per_parent_statistics, frame, child_index, ~0U, blocks);//, root.thread_id, blocks); @@ -966,14 +948,14 @@ extern "C" { } ++_root.depth; - }, ::std::ref(root))); + }, std::ref(root))); } int j = 0, n = static_cast(statistics_threads.size()); for (auto& thread : statistics_threads) { thread.join(); - progress.store(90 + (10 * ++j) / n, ::std::memory_order_release); + progress.store(90 + (10 * ++j) / n, std::memory_order_release); } } else @@ -984,7 +966,7 @@ extern "C" { 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) + //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(); //}); @@ -994,7 +976,7 @@ extern "C" { { auto& frame = blocks[child_block_index]; - if (descriptors[frame.node->id()]->type() == ::profiler::BlockType::Block) + if (descriptors[frame.node->id()]->type() == profiler::BlockType::Block) ++root.frames_number; if (root.depth < frame.depth) @@ -1005,23 +987,23 @@ extern "C" { ++root.depth; - 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 - progress.store(100, ::std::memory_order_release); + progress.store(100, std::memory_order_release); return blocks_counter; } ////////////////////////////////////////////////////////////////////////// - PROFILER_API bool readDescriptionsFromStream(::std::atomic& progress, ::std::stringstream& inFile, - ::profiler::SerializedData& serialized_descriptors, - ::profiler::descriptors_list_t& descriptors, - ::std::stringstream& _log) + PROFILER_API bool readDescriptionsFromStream(std::atomic& progress, std::istream& inFile, + profiler::SerializedData& serialized_descriptors, + profiler::descriptors_list_t& descriptors, + std::ostream& _log) { - EASY_FUNCTION(::profiler::colors::Cyan); + EASY_FUNCTION(profiler::colors::Cyan); progress.store(0); @@ -1080,7 +1062,7 @@ extern "C" { char* data = serialized_descriptors[i]; inFile.read(data, sz); - auto descriptor = reinterpret_cast<::profiler::SerializedBlockDescriptor*>(data); + auto descriptor = reinterpret_cast(data); descriptors.push_back(descriptor); i += sz; @@ -1094,6 +1076,114 @@ extern "C" { } ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + + PROFILER_API profiler::block_index_t writeTreesToFile(std::atomic& progress, const char* filename, + const profiler::SerializedData& serialized_descriptors, + profiler::block_id_t descriptors_count, + const profiler::thread_blocks_tree_t& trees, + profiler::block_getter_fn block_getter, + profiler::timestamp_t begin_time, + profiler::timestamp_t end_time, + profiler::processid_t pid, + std::ostream& log) + { + if (!update_progress_write(progress, 0, log)) + { + return 0; + } + + std::ofstream outFile(filename, std::fstream::binary); + if (!outFile.is_open()) + { + log << "Can not open file " << filename; + return 0; + } + + // Write data to file + auto result = writeTreesToStream(progress, outFile, serialized_descriptors, descriptors_count, trees, std::move(block_getter), + begin_time, end_time, pid, log); + + return result; + } + + ////////////////////////////////////////////////////////////////////////// + + PROFILER_API profiler::block_index_t writeTreesToStream(std::atomic& progress, std::ostream& str, + const profiler::SerializedData& serialized_descriptors, + profiler::block_id_t descriptors_count, + const profiler::thread_blocks_tree_t& trees, + profiler::block_getter_fn block_getter, + profiler::timestamp_t begin_time, + profiler::timestamp_t end_time, + profiler::processid_t pid, + std::ostream& log) + { + if (trees.empty() || serialized_descriptors.empty() || descriptors_count == 0) + { + log << "Nothing to save"; + return 0; + } + + using ranges_t = std::unordered_map >; + ranges_t block_ranges; + + for (const auto& kv : trees) + { + const auto id = kv.first; + const auto& tree = kv.second; + + const auto children_size = tree.children.size(); + const auto csitches_size = tree.sync.size(); + BlocksAndCSwitchesRange ranges {{children_size, children_size}, {csitches_size, csitches_size}}; + + auto first_it = std::lower_bound(tree.children.begin(), tree.children.end(), begin_time, [&](profiler::block_index_t element, profiler::timestamp_t value) + { + block_getter(element).node->begin() < value; + }); + + if (first_it != tree.children.end()) + { + auto last_it = std::lower_bound(tree.children.begin(), tree.children.end(), end_time, [&](profiler::block_index_t element, profiler::timestamp_t value) + { + block_getter(element).node->end() < value; + }); + + if (last_it != tree.children.end()) + { + + } + } + + block_ranges[id] = ranges; + } + + + write(str, PROFILER_SIGNATURE); + write(str, EASY_CURRENT_VERSION); + write(str, pid); + + // write 0 because we do not need to oncvert time from ticks to nanoseconds (it's already converted here) + write(str, 0LL); // CPU frequency + + write(str, begin_time); + write(str, end_time); + + uint64_t usedMemorySize = 0; // memory size used by profiler blocks + profiler::block_index_t blocksCount = 0; + + write(str, usedMemorySize); + write(str, serialized_descriptors.size()); + write(str, blocksCount); + write(str, descriptors_count); + + log << "Not implemented"; + progress.store(100, std::memory_order_release); + return blocksCount; + } + + ////////////////////////////////////////////////////////////////////////// } diff --git a/profiler_gui/blocks_graphics_view.cpp b/profiler_gui/blocks_graphics_view.cpp index c89037f..7cd7463 100644 --- a/profiler_gui/blocks_graphics_view.cpp +++ b/profiler_gui/blocks_graphics_view.cpp @@ -614,6 +614,31 @@ const BlocksGraphicsView::Items &BlocksGraphicsView::getItems() const return m_items; } +bool BlocksGraphicsView::getSelectionRegionForSaving(profiler::timestamp_t& _beginTime, profiler::timestamp_t& _endTime) const +{ + if (m_selectedBlocks.empty()) + return false; + + _beginTime = ~0ULL; + _endTime = 0; + + for (const auto& selection : m_selectedBlocks) + { + const auto& tree = easyBlocksTree(selection.tree); + _beginTime = std::min(_beginTime, tree.node->begin()); + _endTime = std::max(_endTime, tree.node->end()); + } + + if (_beginTime > 10) + _beginTime -= 10; + else + _beginTime = 0; + + _endTime += 10; + + return true; +} + qreal BlocksGraphicsView::setTree(BlocksGraphicsItem* _item, const ::profiler::BlocksTree::children_t& _children, qreal& _height, uint32_t& _maxDepthChild, qreal _y, short _level) { if (_children.empty()) diff --git a/profiler_gui/blocks_graphics_view.h b/profiler_gui/blocks_graphics_view.h index 5f44089..04b1017 100644 --- a/profiler_gui/blocks_graphics_view.h +++ b/profiler_gui/blocks_graphics_view.h @@ -190,6 +190,8 @@ public: const Items& getItems() const; + bool getSelectionRegionForSaving(profiler::timestamp_t& _beginTime, profiler::timestamp_t& _endTime) const; + void inspectCurrentView(bool _strict) { onInspectCurrentView(_strict); } diff --git a/profiler_gui/common_types.h b/profiler_gui/common_types.h index bfa384b..b43dcd5 100644 --- a/profiler_gui/common_types.h +++ b/profiler_gui/common_types.h @@ -122,8 +122,6 @@ struct EasyBlock Q_DECL_FINAL { } -private: - EasyBlock(const EasyBlock&) = delete; }; #pragma pack(pop) diff --git a/profiler_gui/globals.cpp b/profiler_gui/globals.cpp index 47ab38c..1c92d69 100644 --- a/profiler_gui/globals.cpp +++ b/profiler_gui/globals.cpp @@ -79,6 +79,7 @@ namespace profiler_gui { Globals::Globals() : theme("default") + , pid(0) , begin_time(0) , selected_thread(0U) , selected_block(::profiler_gui::numeric_max()) diff --git a/profiler_gui/globals.h b/profiler_gui/globals.h index 15f6377..a11a744 100644 --- a/profiler_gui/globals.h +++ b/profiler_gui/globals.h @@ -204,6 +204,7 @@ namespace profiler_gui { SceneData scene; ///< Diagram scene sizes and visible area position SizeGuide size; ///< Various widgets and font sizes adapted to current device pixel ratio + ::profiler::processid_t pid; ///< Profiled process ID ::profiler::timestamp_t begin_time; ///< Timestamp of the most left diagram scene point (x=0) ::profiler::thread_id_t selected_thread; ///< Current selected thread id ::profiler::block_index_t selected_block; ///< Current selected profiler block index @@ -263,6 +264,10 @@ inline profiler::SerializedBlockDescriptor& easyDescriptor(profiler::block_id_t return *EASY_GLOBALS.descriptors[i]; } +inline profiler::SerializedBlockDescriptor& easyDescriptor(const profiler::BlocksTree& _block) { + return easyDescriptor(_block.node->id()); +} + EASY_FORCE_INLINE const profiler::BlocksTree& easyBlocksTree(profiler::block_index_t i) { return easyBlock(i).tree; } diff --git a/profiler_gui/main_window.cpp b/profiler_gui/main_window.cpp index fd60508..6dc2f5f 100644 --- a/profiler_gui/main_window.cpp +++ b/profiler_gui/main_window.cpp @@ -250,7 +250,7 @@ void MainWindow::configureSizes() w.hide(); } -MainWindow::MainWindow() : Parent(), m_theme("default"), m_lastAddress("localhost"), m_lastPort(::profiler::DEFAULT_PORT) +MainWindow::MainWindow() : Parent(), m_theme("default"), m_lastAddress("localhost"), m_lastPort(profiler::DEFAULT_PORT) { { QIcon icon(":/images/logo"); if (!icon.isNull()) QApplication::setWindowIcon(icon); } @@ -378,6 +378,10 @@ MainWindow::MainWindow() : Parent(), m_theme("default"), m_lastAddress("localhos static_cast(m_graphicsView->widget())->view()->inspectCurrentView(true); }); + action = toolbar->addAction(QIcon(imagePath("crop")), "Crop and save"); + action->setToolTip("Crop and save selected area\nas separate .prof file."); + connect(action, &QAction::triggered, this, &This::onCropAndSaveClicked); + toolbar->addSeparator(); auto menu = new QMenu("Settings", this); menu->setToolTipsVisible(true); @@ -547,8 +551,8 @@ MainWindow::MainWindow() : Parent(), m_theme("default"), m_lastAddress("localhos action = new QAction("Chrono text at top", actionGroup); action->setToolTip("Draw duration of selected interval\nat the top of the screen."); action->setCheckable(true); - action->setData(static_cast(::profiler_gui::ChronoTextPosition_Top)); - if (EASY_GLOBALS.chrono_text_position == ::profiler_gui::ChronoTextPosition_Top) + action->setData(static_cast(profiler_gui::ChronoTextPosition_Top)); + if (EASY_GLOBALS.chrono_text_position == profiler_gui::ChronoTextPosition_Top) action->setChecked(true); submenu->addAction(action); connect(action, &QAction::triggered, this, &This::onChronoTextPosChanged); @@ -556,8 +560,8 @@ MainWindow::MainWindow() : Parent(), m_theme("default"), m_lastAddress("localhos action = new QAction("Chrono text at center", actionGroup); action->setToolTip("Draw duration of selected interval\nat the center of the screen."); action->setCheckable(true); - action->setData(static_cast(::profiler_gui::ChronoTextPosition_Center)); - if (EASY_GLOBALS.chrono_text_position == ::profiler_gui::ChronoTextPosition_Center) + action->setData(static_cast(profiler_gui::ChronoTextPosition_Center)); + if (EASY_GLOBALS.chrono_text_position == profiler_gui::ChronoTextPosition_Center) action->setChecked(true); submenu->addAction(action); connect(action, &QAction::triggered, this, &This::onChronoTextPosChanged); @@ -565,8 +569,8 @@ MainWindow::MainWindow() : Parent(), m_theme("default"), m_lastAddress("localhos action = new QAction("Chrono text at bottom", actionGroup); action->setToolTip("Draw duration of selected interval\nat the bottom of the screen."); action->setCheckable(true); - action->setData(static_cast(::profiler_gui::ChronoTextPosition_Bottom)); - if (EASY_GLOBALS.chrono_text_position == ::profiler_gui::ChronoTextPosition_Bottom) + action->setData(static_cast(profiler_gui::ChronoTextPosition_Bottom)); + if (EASY_GLOBALS.chrono_text_position == profiler_gui::ChronoTextPosition_Bottom) action->setChecked(true); submenu->addAction(action); connect(action, &QAction::triggered, this, &This::onChronoTextPosChanged); @@ -674,32 +678,32 @@ MainWindow::MainWindow() : Parent(), m_theme("default"), m_lastAddress("localhos actionGroup->setExclusive(true); action = new QAction("Auto", actionGroup); action->setCheckable(true); - action->setData(static_cast(::profiler_gui::TimeUnits_auto)); - if (EASY_GLOBALS.time_units == ::profiler_gui::TimeUnits_auto) + action->setData(static_cast(profiler_gui::TimeUnits_auto)); + if (EASY_GLOBALS.time_units == profiler_gui::TimeUnits_auto) action->setChecked(true); submenu->addAction(action); connect(action, &QAction::triggered, this, &This::onUnitsChanged); action = new QAction("Milliseconds", actionGroup); action->setCheckable(true); - action->setData(static_cast(::profiler_gui::TimeUnits_ms)); - if (EASY_GLOBALS.time_units == ::profiler_gui::TimeUnits_ms) + action->setData(static_cast(profiler_gui::TimeUnits_ms)); + if (EASY_GLOBALS.time_units == profiler_gui::TimeUnits_ms) action->setChecked(true); submenu->addAction(action); connect(action, &QAction::triggered, this, &This::onUnitsChanged); action = new QAction("Microseconds", actionGroup); action->setCheckable(true); - action->setData(static_cast(::profiler_gui::TimeUnits_us)); - if (EASY_GLOBALS.time_units == ::profiler_gui::TimeUnits_us) + action->setData(static_cast(profiler_gui::TimeUnits_us)); + if (EASY_GLOBALS.time_units == profiler_gui::TimeUnits_us) action->setChecked(true); submenu->addAction(action); connect(action, &QAction::triggered, this, &This::onUnitsChanged); action = new QAction("Nanoseconds", actionGroup); action->setCheckable(true); - action->setData(static_cast(::profiler_gui::TimeUnits_ns)); - if (EASY_GLOBALS.time_units == ::profiler_gui::TimeUnits_ns) + action->setData(static_cast(profiler_gui::TimeUnits_ns)); + if (EASY_GLOBALS.time_units == profiler_gui::TimeUnits_ns) action->setChecked(true); submenu->addAction(action); connect(action, &QAction::triggered, this, &This::onUnitsChanged); @@ -789,13 +793,14 @@ MainWindow::MainWindow() : Parent(), m_theme("default"), m_lastAddress("localhos m_frameTimeEdit->setValidator(val); m_frameTimeEdit->setText(QString::number(EASY_GLOBALS.frame_time * 1e-3)); connect(m_frameTimeEdit, &QLineEdit::editingFinished, this, &This::onFrameTimeEditFinish); - connect(&EASY_GLOBALS.events, &::profiler_gui::GlobalSignals::expectedFrameTimeChanged, this, &This::onFrameTimeChanged); + connect(&EASY_GLOBALS.events, &profiler_gui::GlobalSignals::expectedFrameTimeChanged, this, &This::onFrameTimeChanged); toolbar->addWidget(m_frameTimeEdit); lbl = new QLabel("ms", toolbar); lbl->setContentsMargins(5, 2, 1, 1); toolbar->addWidget(lbl); + m_readerTimer.setInterval(LOADER_TIMER_INTERVAL); connect(graphicsView->view(), &BlocksGraphicsView::intervalChanged, treeWidget->tree(), &BlocksTreeWidget::setTreeBlocks); connect(&m_readerTimer, &QTimer::timeout, this, &This::onFileReaderTimeout); @@ -962,14 +967,14 @@ void MainWindow::loadFile(const QString& filename) createProgressDialog(QString("Loading %1...").arg(filename.mid(std::max(i, j) + 1))); - m_readerTimer.start(LOADER_TIMER_INTERVAL); + m_readerTimer.start(); m_reader.load(filename); } void MainWindow::readStream(std::stringstream& _data) { createProgressDialog(tr("Reading from stream...")); - m_readerTimer.start(LOADER_TIMER_INTERVAL); + m_readerTimer.start(); m_reader.load(_data); } @@ -1117,8 +1122,8 @@ void MainWindow::clear() emit EASY_GLOBALS.events.allDataGoingToBeDeleted(); EASY_GLOBALS.selected_thread = 0; - ::profiler_gui::set_max(EASY_GLOBALS.selected_block); - ::profiler_gui::set_max(EASY_GLOBALS.selected_block_id); + profiler_gui::set_max(EASY_GLOBALS.selected_block); + profiler_gui::set_max(EASY_GLOBALS.selected_block_id); EASY_GLOBALS.profiler_blocks.clear(); EASY_GLOBALS.descriptors.clear(); EASY_GLOBALS.gui_blocks.clear(); @@ -1181,14 +1186,14 @@ void MainWindow::onEncodingChanged(bool) void MainWindow::onChronoTextPosChanged(bool) { auto _sender = qobject_cast(sender()); - EASY_GLOBALS.chrono_text_position = static_cast<::profiler_gui::ChronometerTextPosition>(_sender->data().toInt()); + EASY_GLOBALS.chrono_text_position = static_cast(_sender->data().toInt()); refreshDiagram(); } void MainWindow::onUnitsChanged(bool) { auto _sender = qobject_cast(sender()); - EASY_GLOBALS.time_units = static_cast<::profiler_gui::TimeUnits>(_sender->data().toInt()); + EASY_GLOBALS.time_units = static_cast(_sender->data().toInt()); } void MainWindow::onEnableDisableStatistics(bool _checked) @@ -1379,7 +1384,7 @@ void MainWindow::closeEvent(QCloseEvent* close_event) void MainWindow::loadSettings() { - QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME); + QSettings settings(profiler_gui::ORGANAZATION_NAME, profiler_gui::APPLICATION_NAME); settings.beginGroup("main"); auto last_files = settings.value("last_files"); @@ -1400,11 +1405,11 @@ void MainWindow::loadSettings() auto val = settings.value("chrono_text_position"); if (!val.isNull()) - EASY_GLOBALS.chrono_text_position = static_cast<::profiler_gui::ChronometerTextPosition>(val.toInt()); + EASY_GLOBALS.chrono_text_position = static_cast(val.toInt()); val = settings.value("time_units"); if (!val.isNull()) - EASY_GLOBALS.time_units = static_cast<::profiler_gui::TimeUnits>(val.toInt()); + EASY_GLOBALS.time_units = static_cast(val.toInt()); val = settings.value("frame_time"); @@ -1529,7 +1534,7 @@ void MainWindow::loadSettings() void MainWindow::loadGeometry() { - QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME); + QSettings settings(profiler_gui::ORGANAZATION_NAME, profiler_gui::APPLICATION_NAME); settings.beginGroup("main"); auto geometry = settings.value("geometry").toByteArray(); @@ -1559,7 +1564,7 @@ void MainWindow::loadGeometry() void MainWindow::saveSettingsAndGeometry() { - QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME); + QSettings settings(profiler_gui::ORGANAZATION_NAME, profiler_gui::APPLICATION_NAME); settings.beginGroup("main"); settings.setValue("geometry", this->saveGeometry()); @@ -1838,123 +1843,156 @@ void MainWindow::onListenerDialogClose(int _result) ////////////////////////////////////////////////////////////////////////// +void MainWindow::closeProgressDialogAndClearReader() +{ + m_reader.interrupt(); + m_readerTimer.stop(); + destroyProgressDialog(); +} + +void MainWindow::onLoadingFinish(profiler::block_index_t& _nblocks) +{ + _nblocks = m_reader.size(); + if (_nblocks != 0) + { + emit EASY_GLOBALS.events.allDataGoingToBeDeleted(); + + profiler::SerializedData serialized_blocks; + profiler::SerializedData serialized_descriptors; + profiler::descriptors_list_t descriptors; + profiler::blocks_t blocks; + profiler::thread_blocks_tree_t threads_map; + QString filename; + uint32_t descriptorsNumberInFile = 0; + uint32_t version = 0; + profiler::processid_t pid = 0; + + m_reader.get(serialized_blocks, serialized_descriptors, descriptors, blocks, threads_map, + descriptorsNumberInFile, version, pid, filename); + + if (threads_map.size() > 0xff) + { + 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_bNetworkFileRegime = !m_reader.isFile(); + if (!m_bNetworkFileRegime) + { + auto index = m_lastFiles.indexOf(filename, 0); + if (index == -1) + { + // This file is totally new. Add it to the list. + addFileToList(filename); + } + else + { + if (index != 0) + { + // This file has been already loaded. Move it to the front. + m_lastFiles.move(index, 0); + auto fileActions = m_loadActionMenu->actions(); + auto action = fileActions.at(index); + m_loadActionMenu->removeAction(action); + m_loadActionMenu->insertAction(fileActions.front(), action); + validateLastDir(); + } + + m_bOpenedCacheFile = filename.contains(NETWORK_CACHE_FILE); + + if (m_bOpenedCacheFile) + setWindowTitle(QString(EASY_DEFAULT_WINDOW_TITLE " - [%1] - UNSAVED network cache file").arg(filename)); + else + setWindowTitle(QString(EASY_DEFAULT_WINDOW_TITLE " - [%1]").arg(filename)); + } + } + else + { + m_bOpenedCacheFile = false; + setWindowTitle(EASY_DEFAULT_WINDOW_TITLE " - UNSAVED network cache"); + } + + m_serializedBlocks = std::move(serialized_blocks); + m_serializedDescriptors = std::move(serialized_descriptors); + m_descriptorsNumberInFile = descriptorsNumberInFile; + EASY_GLOBALS.selected_thread = 0; + EASY_GLOBALS.version = version; + EASY_GLOBALS.pid = pid; + profiler_gui::set_max(EASY_GLOBALS.selected_block); + profiler_gui::set_max(EASY_GLOBALS.selected_block_id); + EASY_GLOBALS.profiler_blocks.swap(threads_map); + EASY_GLOBALS.descriptors.swap(descriptors); + + EASY_GLOBALS.gui_blocks.clear(); + EASY_GLOBALS.gui_blocks.resize(_nblocks); + memset(EASY_GLOBALS.gui_blocks.data(), 0, sizeof(profiler_gui::EasyBlock) * _nblocks); + + for (std::remove_reference::type i = 0; i < _nblocks; ++i) + { + auto& guiblock = EASY_GLOBALS.gui_blocks[i]; + guiblock.tree = std::move(blocks[i]); +#ifdef EASY_TREE_WIDGET__USE_VECTOR + profiler_gui::set_max(guiblock.tree_item); +#endif + } + + m_saveAction->setEnabled(true); + m_deleteAction->setEnabled(true); + } + else + { + QMessageBox::warning(this, "Warning", QString("Cannot read profiled blocks.\n\nReason:\n%1").arg(m_reader.getError()), QMessageBox::Close); + + if (m_reader.isFile()) + { + auto index = m_lastFiles.indexOf(m_reader.filename(), 0); + if (index >= 0) + { + // Remove unexisting file from list + m_lastFiles.removeAt(index); + auto action = m_loadActionMenu->actions().at(index); + m_loadActionMenu->removeAction(action); + delete action; + validateLastDir(); + } + } + } +} + +void MainWindow::onSavingFinish() +{ + +} + void MainWindow::onFileReaderTimeout() { if (m_reader.done()) { - const auto nblocks = m_reader.size(); - if (nblocks != 0) + if (m_reader.isLoading()) { - emit EASY_GLOBALS.events.allDataGoingToBeDeleted(); + profiler::block_index_t nblocks = 0; - ::profiler::SerializedData serialized_blocks; - ::profiler::SerializedData serialized_descriptors; - ::profiler::descriptors_list_t descriptors; - ::profiler::blocks_t blocks; - ::profiler::thread_blocks_tree_t threads_map; - QString filename; - uint32_t descriptorsNumberInFile = 0; - uint32_t version = 0; - m_reader.get(serialized_blocks, serialized_descriptors, descriptors, blocks, threads_map, descriptorsNumberInFile, version, filename); + onLoadingFinish(nblocks); + closeProgressDialogAndClearReader(); - if (threads_map.size() > 0xff) + if (nblocks != 0) { - 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."; + emit EASY_GLOBALS.events.fileOpened(); + if (EASY_GLOBALS.all_items_expanded_by_default) + onExpandAllClicked(true); } - - m_bNetworkFileRegime = !m_reader.isFile(); - if (!m_bNetworkFileRegime) - { - auto index = m_lastFiles.indexOf(filename, 0); - if (index == -1) - { - // This file is totally new. Add it to the list. - addFileToList(filename); - } - else - { - if (index != 0) - { - // This file has been already loaded. Move it to the front. - m_lastFiles.move(index, 0); - auto fileActions = m_loadActionMenu->actions(); - auto action = fileActions.at(index); - m_loadActionMenu->removeAction(action); - m_loadActionMenu->insertAction(fileActions.front(), action); - validateLastDir(); - } - - m_bOpenedCacheFile = filename.contains(NETWORK_CACHE_FILE); - - if (m_bOpenedCacheFile) - setWindowTitle(QString(EASY_DEFAULT_WINDOW_TITLE " - [%1] - UNSAVED network cache file").arg(filename)); - else - setWindowTitle(QString(EASY_DEFAULT_WINDOW_TITLE " - [%1]").arg(filename)); - } - } - else - { - m_bOpenedCacheFile = false; - setWindowTitle(EASY_DEFAULT_WINDOW_TITLE " - UNSAVED network cache"); - } - - m_serializedBlocks = std::move(serialized_blocks); - m_serializedDescriptors = std::move(serialized_descriptors); - m_descriptorsNumberInFile = descriptorsNumberInFile; - EASY_GLOBALS.selected_thread = 0; - EASY_GLOBALS.version = version; - ::profiler_gui::set_max(EASY_GLOBALS.selected_block); - ::profiler_gui::set_max(EASY_GLOBALS.selected_block_id); - EASY_GLOBALS.profiler_blocks.swap(threads_map); - EASY_GLOBALS.descriptors.swap(descriptors); - - EASY_GLOBALS.gui_blocks.clear(); - EASY_GLOBALS.gui_blocks.resize(nblocks); - memset(EASY_GLOBALS.gui_blocks.data(), 0, sizeof(::profiler_gui::EasyBlock) * nblocks); - for (std::remove_const::type i = 0; i < nblocks; ++i) { - auto& guiblock = EASY_GLOBALS.gui_blocks[i]; - guiblock.tree = std::move(blocks[i]); -#ifdef EASY_TREE_WIDGET__USE_VECTOR - ::profiler_gui::set_max(guiblock.tree_item); -#endif - } - - m_saveAction->setEnabled(true); - m_deleteAction->setEnabled(true); + } + else if (m_reader.isSaving()) + { + onSavingFinish(); + closeProgressDialogAndClearReader(); } else { - QMessageBox::warning(this, "Warning", QString("Cannot read profiled blocks.\n\nReason:\n%1").arg(m_reader.getError()), QMessageBox::Close); - - if (m_reader.isFile()) - { - auto index = m_lastFiles.indexOf(m_reader.filename(), 0); - if (index >= 0) - { - // Remove unexisting file from list - m_lastFiles.removeAt(index); - auto action = m_loadActionMenu->actions().at(index); - m_loadActionMenu->removeAction(action); - delete action; - validateLastDir(); - } - } - } - - m_reader.interrupt(); - - m_readerTimer.stop(); - destroyProgressDialog(); - - if (nblocks != 0) - { - emit EASY_GLOBALS.events.fileOpened(); - if (EASY_GLOBALS.all_items_expanded_by_default) - onExpandAllClicked(true); + closeProgressDialogAndClearReader(); } } else if (m_progress != nullptr) @@ -1987,6 +2025,16 @@ const bool FileReader::isFile() const return m_isFile; } +const bool FileReader::isSaving() const +{ + return m_jobType == JobType::Saving; +} + +const bool FileReader::isLoading() const +{ + return m_jobType == JobType::Loading; +} + bool FileReader::done() const { return m_bDone.load(std::memory_order_acquire); @@ -2011,13 +2059,20 @@ void FileReader::load(const QString& _filename) { interrupt(); + m_jobType = JobType::Loading; m_isFile = true; m_filename = _filename; - m_thread = 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, m_descriptorsNumberInFile, m_version, _enableStatistics, m_errorMessage), std::memory_order_release); + + m_thread = 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, + m_descriptorsNumberInFile, m_version, m_pid, _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); } @@ -2025,6 +2080,7 @@ void FileReader::load(std::stringstream& _stream) { interrupt(); + m_jobType = JobType::Loading; m_isFile = false; m_filename.clear(); @@ -2038,19 +2094,47 @@ void FileReader::load(std::stringstream& _stream) m_stream.swap(_stream); #endif - m_thread = std::thread([this](bool _enableStatistics) { + m_thread = std::thread([this](bool _enableStatistics) + { std::ofstream cache_file(NETWORK_CACHE_FILE, std::fstream::binary); - if (cache_file.is_open()) { + if (cache_file.is_open()) + { cache_file << m_stream.str(); cache_file.close(); } - m_size.store(fillTreesFromStream(m_progress, m_stream, m_serializedBlocks, m_serializedDescriptors, m_descriptors, - m_blocks, m_blocksTree, m_descriptorsNumberInFile, m_version, _enableStatistics, m_errorMessage), std::memory_order_release); + + m_size.store(fillTreesFromStream(m_progress, m_stream, m_serializedBlocks, m_serializedDescriptors, + m_descriptors, m_blocks, m_blocksTree, m_descriptorsNumberInFile, + m_version, m_pid, _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); } +void FileReader::save(const QString& _filename, profiler::timestamp_t _beginTime, profiler::timestamp_t _endTime, + const profiler::SerializedData& _serializedDescriptors, profiler::block_id_t descriptors_count, + const profiler::thread_blocks_tree_t& _trees, profiler::block_getter_fn block_getter, + profiler::processid_t _pid) +{ + interrupt(); + + m_jobType = JobType::Saving; + m_isFile = true; + m_filename = _filename; + + auto serializedDescriptors = std::ref(_serializedDescriptors); + auto trees = std::ref(_trees); + + m_thread = std::thread([=] (profiler::block_getter_fn getter) { + writeTreesToFile(m_progress, m_filename.toStdString().c_str(), serializedDescriptors, descriptors_count, trees, + getter, _beginTime, _endTime, _pid, m_errorMessage); + m_progress.store(100, std::memory_order_release); + m_bDone.store(true, std::memory_order_release); + }, std::move(block_getter)); +} + void FileReader::interrupt() { join(); @@ -2064,25 +2148,29 @@ void FileReader::interrupt() m_blocksTree.clear(); m_descriptorsNumberInFile = 0; m_version = 0; + m_pid = 0; + m_jobType = JobType::Idle; clear_stream(m_stream); clear_stream(m_errorMessage); } -void FileReader::get(::profiler::SerializedData& _serializedBlocks, ::profiler::SerializedData& _serializedDescriptors, - ::profiler::descriptors_list_t& _descriptors, ::profiler::blocks_t& _blocks, - ::profiler::thread_blocks_tree_t& _tree, uint32_t& _descriptorsNumberInFile, uint32_t& _version, QString& _filename) +void FileReader::get(profiler::SerializedData& _serializedBlocks, profiler::SerializedData& _serializedDescriptors, + profiler::descriptors_list_t& _descriptors, profiler::blocks_t& _blocks, + profiler::thread_blocks_tree_t& _trees, uint32_t& _descriptorsNumberInFile, uint32_t& _version, + profiler::processid_t& _pid, QString& _filename) { if (done()) { m_serializedBlocks.swap(_serializedBlocks); m_serializedDescriptors.swap(_serializedDescriptors); - ::profiler::descriptors_list_t(std::move(m_descriptors)).swap(_descriptors); + profiler::descriptors_list_t(std::move(m_descriptors)).swap(_descriptors); m_blocks.swap(_blocks); - m_blocksTree.swap(_tree); + m_blocksTree.swap(_trees); m_filename.swap(_filename); _descriptorsNumberInFile = m_descriptorsNumberInFile; _version = m_version; + _pid = m_pid; } } @@ -2126,12 +2214,12 @@ void MainWindow::onFrameTimeEditFinish() EASY_GLOBALS.frame_time = text.toFloat() * 1e3f; - disconnect(&EASY_GLOBALS.events, &::profiler_gui::GlobalSignals::expectedFrameTimeChanged, + disconnect(&EASY_GLOBALS.events, &profiler_gui::GlobalSignals::expectedFrameTimeChanged, this, &This::onFrameTimeChanged); emit EASY_GLOBALS.events.expectedFrameTimeChanged(); - connect(&EASY_GLOBALS.events, &::profiler_gui::GlobalSignals::expectedFrameTimeChanged, + connect(&EASY_GLOBALS.events, &profiler_gui::GlobalSignals::expectedFrameTimeChanged, this, &This::onFrameTimeChanged); } @@ -2142,6 +2230,36 @@ void MainWindow::onFrameTimeChanged() ////////////////////////////////////////////////////////////////////////// +void MainWindow::onCropAndSaveClicked(bool) +{ + profiler::timestamp_t beginTime = 0, endTime = 0; + const bool hasSelection = static_cast(m_graphicsView->widget())->view()->getSelectionRegionForSaving(beginTime, endTime); + if (!hasSelection) + return; + + QString lastFile = m_lastFiles.empty() ? QString() : m_lastFiles.front(); + + const auto i = lastFile.lastIndexOf(QChar('/')); + const auto j = lastFile.lastIndexOf(QChar('\\')); + auto k = std::max(i, j); + + QString dir; + if (k > 0) + dir = lastFile.mid(0, ++k); + + auto filename = QFileDialog::getSaveFileName(this, "Save cropped area to EasyProfiler File", dir, + "EasyProfiler File (*.prof);;All Files (*.*)"); + if (filename.isEmpty()) + return; + + createProgressDialog(tr("Saving selected region...")); + m_readerTimer.start(); + m_reader.save(filename, beginTime, endTime, m_serializedDescriptors, m_descriptorsNumberInFile, + EASY_GLOBALS.profiler_blocks, easyBlocksTree, EASY_GLOBALS.pid); +} + +////////////////////////////////////////////////////////////////////////// + void MainWindow::onConnectClicked(bool) { if (EASY_GLOBALS.connected) @@ -2428,7 +2546,7 @@ void MainWindow::onGetBlockDescriptionsClicked(bool) ////////////////////////////////////////////////////////////////////////// -void MainWindow::onBlockStatusChange(::profiler::block_id_t _id, ::profiler::EasyBlockStatus _status) +void MainWindow::onBlockStatusChange(profiler::block_id_t _id, profiler::EasyBlockStatus _status) { if (EASY_GLOBALS.connected) m_listener.send(profiler::net::BlockStatusMessage(_id, static_cast(_status))); @@ -2613,7 +2731,7 @@ bool SocketListener::connect(const char* _ipaddress, uint16_t _port, profiler::n seek += bytes; } - auto message = reinterpret_cast(buffer); + auto message = reinterpret_cast(buffer); if (message->isEasyNetMessage() && message->type == profiler::net::MessageType::Connection_Accepted) _reply = *message; @@ -2625,7 +2743,7 @@ bool SocketListener::connect(const char* _ipaddress, uint16_t _port, profiler::n return isConnected; } -bool SocketListener::reconnect(const char* _ipaddress, uint16_t _port, ::profiler::net::EasyProfilerStatus& _reply) +bool SocketListener::reconnect(const char* _ipaddress, uint16_t _port, profiler::net::EasyProfilerStatus& _reply) { return connect(_ipaddress, _port, _reply, true); } @@ -2820,7 +2938,7 @@ void SocketListener::listenCapture() if (bytes > 0) { - auto message = reinterpret_cast(buf); + auto message = reinterpret_cast(buf); if (!message->isEasyNetMessage()) continue; @@ -2978,7 +3096,7 @@ void SocketListener::listenDescription() if (bytes > 0) { - auto message = reinterpret_cast(buf); + auto message = reinterpret_cast(buf); if (!message->isEasyNetMessage()) continue; @@ -3069,7 +3187,7 @@ void SocketListener::listenDescription() void SocketListener::listenFrameTime() { - EASY_STATIC_CONSTEXPR size_t buffer_size = sizeof(::profiler::net::TimestampMessage) << 2; + EASY_STATIC_CONSTEXPR size_t buffer_size = sizeof(profiler::net::TimestampMessage) << 2; char buffer[buffer_size] = {}; int seek = 0, bytes = 0; @@ -3108,7 +3226,7 @@ void SocketListener::listenFrameTime() if (bytes > 0) { - auto message = reinterpret_cast(buf); + auto message = reinterpret_cast(buf); if (!message->isEasyNetMessage()) continue; diff --git a/profiler_gui/main_window.h b/profiler_gui/main_window.h index bfeb21a..f6f2dbe 100644 --- a/profiler_gui/main_window.h +++ b/profiler_gui/main_window.h @@ -54,10 +54,10 @@ #ifndef EASY_PROFILER_GUI__MAIN_WINDOW__H #define EASY_PROFILER_GUI__MAIN_WINDOW__H -#include -#include #include #include +#include +#include #include #include @@ -85,6 +85,13 @@ namespace profiler { namespace net { struct EasyProfilerStatus; } } class FileReader Q_DECL_FINAL { + enum class JobType : int8_t + { + Idle=0, + Loading, + Saving, + }; + profiler::SerializedData m_serializedBlocks; ///< profiler::SerializedData m_serializedDescriptors; ///< profiler::descriptors_list_t m_descriptors; ///< @@ -93,12 +100,14 @@ class FileReader Q_DECL_FINAL std::stringstream m_stream; ///< std::stringstream m_errorMessage; ///< QString m_filename; ///< + profiler::processid_t m_pid = 0; ///< uint32_t m_descriptorsNumberInFile = 0; ///< uint32_t m_version = 0; ///< std::thread m_thread; ///< std::atomic_bool m_bDone; ///< std::atomic m_progress; ///< - std::atomic m_size; ///< + std::atomic m_size; ///< + JobType m_jobType = JobType::Idle; ///< bool m_isFile = false; ///< public: @@ -107,6 +116,9 @@ public: ~FileReader(); const bool isFile() const; + const bool isSaving() const; + const bool isLoading() const; + bool done() const; int progress() const; unsigned int size() const; @@ -114,10 +126,17 @@ public: void load(const QString& _filename); void load(std::stringstream& _stream); + + /** \brief Save data to file. + */ + void save(const QString& _filename, profiler::timestamp_t _beginTime, profiler::timestamp_t _endTime, + const profiler::SerializedData& _serializedDescriptors, profiler::block_id_t descriptors_count, + const profiler::thread_blocks_tree_t& _trees, profiler::block_getter_fn block_getter, profiler::processid_t _pid); + 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, - uint32_t& _descriptorsNumberInFile, uint32_t& _version, QString& _filename); + profiler::descriptors_list_t& _descriptors, profiler::blocks_t& _blocks, profiler::thread_blocks_tree_t& _trees, + uint32_t& _descriptorsNumberInFile, uint32_t& _version, profiler::processid_t& _pid, QString& _filename); void join(); @@ -238,42 +257,43 @@ protected: QStringList m_lastFiles; QString m_theme; QString m_lastAddress; - QDockWidget* m_treeWidget = nullptr; - QDockWidget* m_graphicsView = nullptr; - QDockWidget* m_fpsViewer = nullptr; + + QDockWidget* m_treeWidget = nullptr; + QDockWidget* m_graphicsView = nullptr; + QDockWidget* m_fpsViewer = nullptr; #if EASY_GUI_USE_DESCRIPTORS_DOCK_WINDOW != 0 QDockWidget* m_descTreeWidget = nullptr; #endif - class QProgressDialog* m_progress = nullptr; - class BlockDescriptorsWidget* m_dialogDescTree = nullptr; - class QMessageBox* m_listenerDialog = nullptr; + class QProgressDialog* m_progress = nullptr; + class BlockDescriptorsWidget* m_dialogDescTree = nullptr; + class QMessageBox* m_listenerDialog = nullptr; QTimer m_readerTimer; QTimer m_listenerTimer; QTimer m_fpsRequestTimer; - profiler::SerializedData m_serializedBlocks; - profiler::SerializedData m_serializedDescriptors; - FileReader m_reader; - SocketListener m_listener; + profiler::SerializedData m_serializedBlocks; + profiler::SerializedData m_serializedDescriptors; + FileReader m_reader; + SocketListener m_listener; - class QLineEdit* m_addressEdit = nullptr; - class QLineEdit* m_portEdit = nullptr; + class QLineEdit* m_addressEdit = nullptr; + class QLineEdit* m_portEdit = nullptr; class QLineEdit* m_frameTimeEdit = nullptr; class QMenu* m_loadActionMenu = nullptr; - class QAction* m_saveAction = nullptr; - class QAction* m_deleteAction = nullptr; + class QAction* m_saveAction = nullptr; + class QAction* m_deleteAction = nullptr; - class QAction* m_captureAction = nullptr; - class QAction* m_connectAction = nullptr; - class QAction* m_eventTracingEnableAction = nullptr; + class QAction* m_captureAction = nullptr; + class QAction* m_connectAction = nullptr; + class QAction* m_eventTracingEnableAction = nullptr; class QAction* m_eventTracingPriorityAction = nullptr; uint32_t m_descriptorsNumberInFile = 0; - uint16_t m_lastPort = 0; - bool m_bNetworkFileRegime = false; - bool m_bOpenedCacheFile = false; + uint16_t m_lastPort = 0; + bool m_bNetworkFileRegime = false; + bool m_bOpenedCacheFile = false; public: @@ -326,6 +346,7 @@ protected slots: void onEventTracingEnableChange(bool _checked); void onFrameTimeEditFinish(); void onFrameTimeChanged(); + void onCropAndSaveClicked(bool); void onBlockStatusChange(profiler::block_id_t _id, profiler::EasyBlockStatus _status); @@ -339,6 +360,10 @@ private: // Private non-virtual methods + void closeProgressDialogAndClearReader(); + void onLoadingFinish(profiler::block_index_t& _nblocks); + void onSavingFinish(); + void configureSizes(); void clear(); diff --git a/reader/main.cpp b/reader/main.cpp index 34a9360..3bfea00 100644 --- a/reader/main.cpp +++ b/reader/main.cpp @@ -40,7 +40,7 @@ public: }; -void printTree(TreePrinter& printer, const ::profiler::BlocksTree& tree, int level = 0, profiler::timestamp_t parent_dur = 0, profiler::timestamp_t root_dur = 0) +void printTree(TreePrinter& printer, const profiler::BlocksTree& tree, int level = 0, profiler::timestamp_t parent_dur = 0, profiler::timestamp_t root_dur = 0) { // //if (tree.node){ @@ -72,7 +72,7 @@ void printTree(TreePrinter& printer, const ::profiler::BlocksTree& tree, int lev int main(int argc, char* argv[]) { - ::profiler::thread_blocks_tree_t threaded_trees; + profiler::thread_blocks_tree_t threaded_trees; ::std::string filename;// = "test.prof"; if (argc > 1 && argv[1]) @@ -110,14 +110,15 @@ int main(int argc, char* argv[]) auto start = std::chrono::system_clock::now(); - ::profiler::SerializedData serialized_blocks, serialized_descriptors; - ::profiler::descriptors_list_t descriptors; - ::profiler::blocks_t blocks; - ::std::stringstream errorMessage; + profiler::SerializedData serialized_blocks, serialized_descriptors; + profiler::descriptors_list_t descriptors; + profiler::blocks_t blocks; + std::stringstream errorMessage; uint32_t descriptorsNumberInFile = 0; uint32_t version = 0; + profiler::processid_t pid = 0; auto blocks_counter = fillTreesFromFile(filename.c_str(), serialized_blocks, serialized_descriptors, descriptors, blocks, - threaded_trees, descriptorsNumberInFile, version, true, errorMessage); + threaded_trees, descriptorsNumberInFile, version, pid, true, errorMessage); if (blocks_counter == 0) std::cout << "Can not read blocks from file " << filename.c_str() << "\nReason: " << errorMessage.str();