diff --git a/easy_profiler_converter/reader.cpp b/easy_profiler_converter/reader.cpp index e5bd10d..e3cb760 100644 --- a/easy_profiler_converter/reader.cpp +++ b/easy_profiler_converter/reader.cpp @@ -62,11 +62,12 @@ profiler::block_index_t FileReader::readFile(const std::string& filename) profiler::blocks_t blocks; profiler::thread_blocks_tree_t threaded_trees; + profiler::processid_t pid = 0; uint32_t total_descriptors_number = 0; EASY_CONSTEXPR bool DoNotGatherStats = false; const auto blocks_number = ::fillTreesFromFile(filename.c_str(), serialized_blocks, serialized_descriptors, - descriptors, blocks, threaded_trees, total_descriptors_number, m_version, DoNotGatherStats, m_errorMessage); + descriptors, blocks, threaded_trees, total_descriptors_number, m_version, pid, DoNotGatherStats, m_errorMessage); if (blocks_number == 0) return 0; diff --git a/easy_profiler_core/alignment_helpers.h b/easy_profiler_core/alignment_helpers.h new file mode 100644 index 0000000..619aff9 --- /dev/null +++ b/easy_profiler_core/alignment_helpers.h @@ -0,0 +1,274 @@ +/************************************************ + * creation time : 2017/08/20 + * author : Blake Martin + * email : rationalcoder@gmail.com + ************************************************/ + +/** +Lightweight profiler library for c++ +Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin + +Licensed under either of + * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) + * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +at your option. + +The MIT License + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished + to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. + + +The Apache License, Version 2.0 (the "License"); + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +**/ + +#ifndef EASY_PROFILER_ALIGNMENT_HELPERS_H +#define EASY_PROFILER_ALIGNMENT_HELPERS_H + +#include +#include +#include + +//! Checks if a pointer is aligned. +//! \param ptr The pointer to check. +//! \param alignment The alignement (must be a power of 2) +//! \returns true if the memory is aligned. +//! +template +EASY_FORCE_INLINE bool is_aligned(void* ptr) +{ + static_assert((ALIGNMENT & 1) == 0, "Alignment must be a power of two."); + return ((uintptr_t)ptr & (ALIGNMENT-1)) == 0; +} + +EASY_FORCE_INLINE void unaligned_zero16(void* ptr) +{ +#ifndef EASY_ENABLE_STRICT_ALIGNMENT + *(uint16_t*)ptr = 0; +#else + ((char*)ptr)[0] = 0; + ((char*)ptr)[1] = 0; +#endif +} + +EASY_FORCE_INLINE void unaligned_zero32(void* ptr) +{ +#ifndef EASY_ENABLE_STRICT_ALIGNMENT + *(uint32_t*)ptr = 0; +#else + ((char*)ptr)[0] = 0; + ((char*)ptr)[1] = 0; + ((char*)ptr)[2] = 0; + ((char*)ptr)[3] = 0; +#endif +} + +EASY_FORCE_INLINE void unaligned_zero64(void* ptr) +{ +#ifdef EASY_ENABLE_STRICT_ALIGNMENT + // Assume unaligned is more common. + if (!is_aligned(ptr)) { + ((char*)ptr)[0] = 0; + ((char*)ptr)[1] = 0; + ((char*)ptr)[2] = 0; + ((char*)ptr)[3] = 0; + ((char*)ptr)[4] = 0; + ((char*)ptr)[5] = 0; + ((char*)ptr)[6] = 0; + ((char*)ptr)[7] = 0; + } + else +#endif + + *(uint64_t*)ptr = 0; +} + +template +EASY_FORCE_INLINE void unaligned_store16(void* ptr, T val) +{ + static_assert(sizeof(T) == 2, "16 bit type required."); + +#ifndef EASY_ENABLE_STRICT_ALIGNMENT + *(T*)ptr = val; +#else + const char* const temp = (const char*)&val; + ((char*)ptr)[0] = temp[0]; + ((char*)ptr)[1] = temp[1]; +#endif +} + +template +EASY_FORCE_INLINE void unaligned_store32(void* ptr, T val) +{ + static_assert(sizeof(T) == 4, "32 bit type required."); + +#ifndef EASY_ENABLE_STRICT_ALIGNMENT + *(T*)ptr = val; +#else + const char* const temp = (const char*)&val; + ((char*)ptr)[0] = temp[0]; + ((char*)ptr)[1] = temp[1]; + ((char*)ptr)[2] = temp[2]; + ((char*)ptr)[3] = temp[3]; +#endif +} + +template +EASY_FORCE_INLINE void unaligned_store64(void* ptr, T val) +{ + static_assert(sizeof(T) == 8, "64 bit type required."); + +#ifdef EASY_ENABLE_STRICT_ALIGNMENT + // Assume unaligned is more common. + if (!is_aligned(ptr)) { + const char* const temp = (const char*)&val; + ((char*)ptr)[0] = temp[0]; + ((char*)ptr)[1] = temp[1]; + ((char*)ptr)[2] = temp[2]; + ((char*)ptr)[3] = temp[3]; + ((char*)ptr)[4] = temp[4]; + ((char*)ptr)[5] = temp[5]; + ((char*)ptr)[6] = temp[6]; + ((char*)ptr)[7] = temp[7]; + } + else +#endif + + *(T*)ptr = val; +} + +template +EASY_FORCE_INLINE T unaligned_load16(const void* ptr) +{ + static_assert(sizeof(T) == 2, "16 bit type required."); + +#ifndef EASY_ENABLE_STRICT_ALIGNMENT + return *(const T*)ptr; +#else + T value; + ((char*)&value)[0] = ((const char*)ptr)[0]; + ((char*)&value)[1] = ((const char*)ptr)[1]; + return value; +#endif +} + +template +EASY_FORCE_INLINE T unaligned_load16(const void* ptr, T* val) +{ + static_assert(sizeof(T) == 2, "16 bit type required."); + +#ifndef EASY_ENABLE_STRICT_ALIGNMENT + *val = *(const T*)ptr; +#else + ((char*)val)[0] = ((const char*)ptr)[0]; + ((char*)val)[1] = ((const char*)ptr)[1]; +#endif + + return *val; +} + +template +EASY_FORCE_INLINE T unaligned_load32(const void* ptr) +{ + static_assert(sizeof(T) == 4, "32 bit type required."); + +#ifndef EASY_ENABLE_STRICT_ALIGNMENT + return *(const T*)ptr; +#else + T value; + ((char*)&value)[0] = ((const char*)ptr)[0]; + ((char*)&value)[1] = ((const char*)ptr)[1]; + ((char*)&value)[2] = ((const char*)ptr)[2]; + ((char*)&value)[3] = ((const char*)ptr)[3]; + return value; +#endif +} + +template +EASY_FORCE_INLINE T unaligned_load32(const void* ptr, T* val) +{ + static_assert(sizeof(T) == 4, "32 bit type required."); + +#ifndef EASY_ENABLE_STRICT_ALIGNMENT + *val = *(const T*)ptr; +#else + ((char*)&val)[0] = ((const char*)ptr)[0]; + ((char*)&val)[1] = ((const char*)ptr)[1]; + ((char*)&val)[2] = ((const char*)ptr)[2]; + ((char*)&val)[3] = ((const char*)ptr)[3]; +#endif + + return *val; +} + +template +EASY_FORCE_INLINE T unaligned_load64(const void* ptr) +{ + static_assert(sizeof(T) == 8, "64 bit type required."); + +#ifdef EASY_ENABLE_STRICT_ALIGNMENT + if (!is_aligned(ptr)) { + T value; + ((char*)&value)[0] = ((const char*)ptr)[0]; + ((char*)&value)[1] = ((const char*)ptr)[1]; + ((char*)&value)[2] = ((const char*)ptr)[2]; + ((char*)&value)[3] = ((const char*)ptr)[3]; + ((char*)&value)[4] = ((const char*)ptr)[4]; + ((char*)&value)[5] = ((const char*)ptr)[5]; + ((char*)&value)[6] = ((const char*)ptr)[6]; + ((char*)&value)[7] = ((const char*)ptr)[7]; + return value; + } +#endif + + return *(const T*)ptr; +} + +template +EASY_FORCE_INLINE T unaligned_load64(const void* ptr, T* val) +{ + static_assert(sizeof(T) == 8, "64 bit type required."); + +#ifdef EASY_ENABLE_STRICT_ALIGNMENT + if (!is_aligned(ptr)) { + ((char*)&val)[0] = ((const char*)ptr)[0]; + ((char*)&val)[1] = ((const char*)ptr)[1]; + ((char*)&val)[2] = ((const char*)ptr)[2]; + ((char*)&val)[3] = ((const char*)ptr)[3]; + ((char*)&val)[4] = ((const char*)ptr)[4]; + ((char*)&val)[5] = ((const char*)ptr)[5]; + ((char*)&val)[6] = ((const char*)ptr)[6]; + ((char*)&val)[7] = ((const char*)ptr)[7]; + } + else +#endif + + *val = *(const T*)ptr; + return *val; +} + +#endif //EASY_PROFILER_ALIGNMENT_HELPERS_H diff --git a/easy_profiler_core/chunk_allocator.h b/easy_profiler_core/chunk_allocator.h index 8e03b50..db21e50 100644 --- a/easy_profiler_core/chunk_allocator.h +++ b/easy_profiler_core/chunk_allocator.h @@ -45,9 +45,8 @@ The Apache License, Version 2.0 (the "License"); #include #include -#include -#include #include "outstream.h" +#include "alignment_helpers.h" ////////////////////////////////////////////////////////////////////////// @@ -83,226 +82,6 @@ The Apache License, Version 2.0 (the "License"); ////////////////////////////////////////////////////////////////////////// -//! Checks if a pointer is aligned. -//! \param ptr The pointer to check. -//! \param alignment The alignement (must be a power of 2) -//! \returns true if the memory is aligned. -//! -template -EASY_FORCE_INLINE bool is_aligned(void* ptr) -{ - static_assert((ALIGNMENT & 1) == 0, "Alignment must be a power of two."); - return ((uintptr_t)ptr & (ALIGNMENT-1)) == 0; -} - -EASY_FORCE_INLINE void unaligned_zero16(void* ptr) -{ -#ifndef EASY_ENABLE_STRICT_ALIGNMENT - *(uint16_t*)ptr = 0; -#else - ((char*)ptr)[0] = 0; - ((char*)ptr)[1] = 0; -#endif -} - -EASY_FORCE_INLINE void unaligned_zero32(void* ptr) -{ -#ifndef EASY_ENABLE_STRICT_ALIGNMENT - *(uint32_t*)ptr = 0; -#else - ((char*)ptr)[0] = 0; - ((char*)ptr)[1] = 0; - ((char*)ptr)[2] = 0; - ((char*)ptr)[3] = 0; -#endif -} - -EASY_FORCE_INLINE void unaligned_zero64(void* ptr) -{ -#ifdef EASY_ENABLE_STRICT_ALIGNMENT - // Assume unaligned is more common. - if (!is_aligned(ptr)) { - ((char*)ptr)[0] = 0; - ((char*)ptr)[1] = 0; - ((char*)ptr)[2] = 0; - ((char*)ptr)[3] = 0; - ((char*)ptr)[4] = 0; - ((char*)ptr)[5] = 0; - ((char*)ptr)[6] = 0; - ((char*)ptr)[7] = 0; - } - else -#endif - - *(uint64_t*)ptr = 0; -} - -template -EASY_FORCE_INLINE void unaligned_store16(void* ptr, T val) -{ - static_assert(sizeof(T) == 2, "16 bit type required."); - -#ifndef EASY_ENABLE_STRICT_ALIGNMENT - *(T*)ptr = val; -#else - const char* const temp = (const char*)&val; - ((char*)ptr)[0] = temp[0]; - ((char*)ptr)[1] = temp[1]; -#endif -} - -template -EASY_FORCE_INLINE void unaligned_store32(void* ptr, T val) -{ - static_assert(sizeof(T) == 4, "32 bit type required."); - -#ifndef EASY_ENABLE_STRICT_ALIGNMENT - *(T*)ptr = val; -#else - const char* const temp = (const char*)&val; - ((char*)ptr)[0] = temp[0]; - ((char*)ptr)[1] = temp[1]; - ((char*)ptr)[2] = temp[2]; - ((char*)ptr)[3] = temp[3]; -#endif -} - -template -EASY_FORCE_INLINE void unaligned_store64(void* ptr, T val) -{ - static_assert(sizeof(T) == 8, "64 bit type required."); - -#ifdef EASY_ENABLE_STRICT_ALIGNMENT - // Assume unaligned is more common. - if (!is_aligned(ptr)) { - const char* const temp = (const char*)&val; - ((char*)ptr)[0] = temp[0]; - ((char*)ptr)[1] = temp[1]; - ((char*)ptr)[2] = temp[2]; - ((char*)ptr)[3] = temp[3]; - ((char*)ptr)[4] = temp[4]; - ((char*)ptr)[5] = temp[5]; - ((char*)ptr)[6] = temp[6]; - ((char*)ptr)[7] = temp[7]; - } - else -#endif - - *(T*)ptr = val; -} - -template -EASY_FORCE_INLINE T unaligned_load16(const void* ptr) -{ - static_assert(sizeof(T) == 2, "16 bit type required."); - -#ifndef EASY_ENABLE_STRICT_ALIGNMENT - return *(const T*)ptr; -#else - T value; - ((char*)&value)[0] = ((const char*)ptr)[0]; - ((char*)&value)[1] = ((const char*)ptr)[1]; - return value; -#endif -} - -template -EASY_FORCE_INLINE T unaligned_load16(const void* ptr, T* val) -{ - static_assert(sizeof(T) == 2, "16 bit type required."); - -#ifndef EASY_ENABLE_STRICT_ALIGNMENT - *val = *(const T*)ptr; -#else - ((char*)val)[0] = ((const char*)ptr)[0]; - ((char*)val)[1] = ((const char*)ptr)[1]; -#endif - - return *val; -} - -template -EASY_FORCE_INLINE T unaligned_load32(const void* ptr) -{ - static_assert(sizeof(T) == 4, "32 bit type required."); - -#ifndef EASY_ENABLE_STRICT_ALIGNMENT - return *(const T*)ptr; -#else - T value; - ((char*)&value)[0] = ((const char*)ptr)[0]; - ((char*)&value)[1] = ((const char*)ptr)[1]; - ((char*)&value)[2] = ((const char*)ptr)[2]; - ((char*)&value)[3] = ((const char*)ptr)[3]; - return value; -#endif -} - -template -EASY_FORCE_INLINE T unaligned_load32(const void* ptr, T* val) -{ - static_assert(sizeof(T) == 4, "32 bit type required."); - -#ifndef EASY_ENABLE_STRICT_ALIGNMENT - *val = *(const T*)ptr; -#else - ((char*)&val)[0] = ((const char*)ptr)[0]; - ((char*)&val)[1] = ((const char*)ptr)[1]; - ((char*)&val)[2] = ((const char*)ptr)[2]; - ((char*)&val)[3] = ((const char*)ptr)[3]; -#endif - - return *val; -} - -template -EASY_FORCE_INLINE T unaligned_load64(const void* ptr) -{ - static_assert(sizeof(T) == 8, "64 bit type required."); - -#ifdef EASY_ENABLE_STRICT_ALIGNMENT - if (!is_aligned(ptr)) { - T value; - ((char*)&value)[0] = ((const char*)ptr)[0]; - ((char*)&value)[1] = ((const char*)ptr)[1]; - ((char*)&value)[2] = ((const char*)ptr)[2]; - ((char*)&value)[3] = ((const char*)ptr)[3]; - ((char*)&value)[4] = ((const char*)ptr)[4]; - ((char*)&value)[5] = ((const char*)ptr)[5]; - ((char*)&value)[6] = ((const char*)ptr)[6]; - ((char*)&value)[7] = ((const char*)ptr)[7]; - return value; - } -#endif - - return *(const T*)ptr; -} - -template -EASY_FORCE_INLINE T unaligned_load64(const void* ptr, T* val) -{ - static_assert(sizeof(T) == 8, "64 bit type required."); - -#ifdef EASY_ENABLE_STRICT_ALIGNMENT - if (!is_aligned(ptr)) { - ((char*)&val)[0] = ((const char*)ptr)[0]; - ((char*)&val)[1] = ((const char*)ptr)[1]; - ((char*)&val)[2] = ((const char*)ptr)[2]; - ((char*)&val)[3] = ((const char*)ptr)[3]; - ((char*)&val)[4] = ((const char*)ptr)[4]; - ((char*)&val)[5] = ((const char*)ptr)[5]; - ((char*)&val)[6] = ((const char*)ptr)[6]; - ((char*)&val)[7] = ((const char*)ptr)[7]; - } - else -#endif - - *val = *(const T*)ptr; - return *val; -} - -////////////////////////////////////////////////////////////////////////// - template class chunk_allocator { diff --git a/easy_profiler_core/include/easy/reader.h b/easy_profiler_core/include/easy/reader.h index 50a14f4..aac2322 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,93 +275,49 @@ 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; ////////////////////////////////////////////////////////////////////////// class PROFILER_API SerializedData EASY_FINAL { - char* m_data; - size_t m_size; + uint64_t m_size; + char* m_data; public: SerializedData(const SerializedData&) = delete; SerializedData& operator = (const SerializedData&) = delete; - SerializedData() : m_data(nullptr), m_size(0) - { - } + SerializedData(); - SerializedData(SerializedData&& that) : m_data(that.m_data), m_size(that.m_size) - { - that.m_data = nullptr; - that.m_size = 0; - } + SerializedData(SerializedData&& that); - ~SerializedData() - { - clear(); - } + ~SerializedData(); void set(uint64_t _size); + void extend(uint64_t _size); - SerializedData& operator = (SerializedData&& that) - { - set(that.m_data, that.m_size); - that.m_data = nullptr; - that.m_size = 0; - return *this; - } + SerializedData& operator = (SerializedData&& that); - char* operator [] (uint64_t i) - { - return m_data + i; - } + char* operator [] (uint64_t i); - const char* operator [] (uint64_t i) const - { - return m_data + i; - } + const char* operator [] (uint64_t i) const; - bool empty() const - { - return m_size == 0; - } + bool empty() const; - uint64_t size() const - { - return m_size; - } + uint64_t size() const; - char* data() - { - return m_data; - } + char* data(); - const char* data() const - { - return m_data; - } + const char* data() const; - void clear() - { - set(nullptr, 0); - } + void clear(); - void swap(SerializedData& other) - { - char* d = other.m_data; - uint64_t sz = other.m_size; - - other.m_data = m_data; - other.m_size = m_size; - - m_data = d; - m_size = (size_t)sz; - } + void swap(SerializedData& other); private: @@ -367,59 +327,117 @@ 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, + const profiler::descriptors_list_t& 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, + const profiler::descriptors_list_t& 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, + const profiler::descriptors_list_t& 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, 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, + const profiler::descriptors_list_t& 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, 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..fb45a90 100644 --- a/easy_profiler_core/reader.cpp +++ b/easy_profiler_core/reader.cpp @@ -66,22 +66,19 @@ * : limitations under the License. ************************************************************************/ -#include -#include - -#include "hashed_cstr.h" - -#include #include -#include #include #include #include #include -////////////////////////////////////////////////////////////////////////// +#include +#include -typedef uint64_t processid_t; +#include "hashed_cstr.h" +#include "alignment_helpers.h" + +////////////////////////////////////////////////////////////////////////// extern const uint32_t PROFILER_SIGNATURE; extern const uint32_t EASY_CURRENT_VERSION; @@ -128,26 +125,30 @@ inline bool isCompatibleVersion(uint32_t _version) return _version >= MIN_COMPATIBLE_VERSION; } -inline void write(::std::stringstream& _stream, const char* _value, size_t _size) -{ - _stream.write(_value, _size); -} - -template -inline void write(::std::stringstream& _stream, const T& _value) -{ - _stream.write((const char*)&_value, sizeof(T)); -} - ////////////////////////////////////////////////////////////////////////// namespace profiler { + SerializedData::SerializedData() : m_size(0), m_data(nullptr) + { + } + + SerializedData::SerializedData(SerializedData&& that) : m_size(that.m_size), m_data(that.m_data) + { + that.m_size = 0; + that.m_data = nullptr; + } + + SerializedData::~SerializedData() + { + clear(); + } + void SerializedData::set(char* _data, uint64_t _size) { delete [] m_data; - m_data = _data; m_size = _size; + m_data = _data; } void SerializedData::set(uint64_t _size) @@ -160,8 +161,8 @@ namespace profiler { void SerializedData::extend(uint64_t _size) { - auto olddata = m_data; auto oldsize = m_size; + auto olddata = m_data; m_size = oldsize + _size; m_data = new char[m_size]; @@ -172,6 +173,61 @@ namespace profiler { } } + SerializedData& SerializedData::operator = (SerializedData&& that) + { + set(that.m_data, that.m_size); + that.m_size = 0; + that.m_data = nullptr; + return *this; + } + + char* SerializedData::operator [] (uint64_t i) + { + return m_data + i; + } + + const char* SerializedData::operator [] (uint64_t i) const + { + return m_data + i; + } + + bool SerializedData::empty() const + { + return m_size == 0; + } + + uint64_t SerializedData::size() const + { + return m_size; + } + + char* SerializedData::data() + { + return m_data; + } + + const char* SerializedData::data() const + { + return m_data; + } + + void SerializedData::clear() + { + set(nullptr, 0); + } + + void SerializedData::swap(SerializedData& other) + { + char* d = other.m_data; + const auto sz = other.m_size; + + other.m_data = m_data; + other.m_size = m_size; + + m_data = d; + m_size = sz; + } + extern "C" PROFILER_API void release_stats(BlockStatistics*& _stats) { if (_stats == nullptr) @@ -183,26 +239,26 @@ namespace profiler { _stats = nullptr; } -} +} // end of 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 +276,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 +318,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 +331,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 +372,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 +386,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 +398,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 +410,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 +438,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 +452,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 +491,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) @@ -500,603 +533,986 @@ 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) +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, + profiler::processid_t& pid, + bool gather_statistics, + std::ostream& _log) +{ + if (!update_progress(progress, 0, _log)) { - if (!update_progress(progress, 0, _log)) - { - return 0; - } - - ::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); - - return result; + return 0; } - ////////////////////////////////////////////////////////////////////////// - - 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) + std::ifstream inFile(filename, std::fstream::binary); + if (!inFile.is_open()) { - EASY_FUNCTION(::profiler::colors::Cyan); + _log << "Can not open file " << filename; + return 0; + } - if (!update_progress(progress, 0, _log)) + // Read data from file + auto result = fillTreesFromStream(progress, inFile, serialized_blocks, serialized_descriptors, descriptors, blocks, + threaded_trees, total_descriptors_number, version, pid, gather_statistics, _log); + + return result; +} + +////////////////////////////////////////////////////////////////////////// + +extern "C" 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); + + if (!update_progress(progress, 0, _log)) + { + return 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; + } + + 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; + } + + EasyFileHeader header; + header.signature = signature; + header.version = version; + + if (version < EASY_V_200) + { + if (!readHeader_v1(header, inFile, _log)) + return 0; + } + else + { + if (!readHeader_v2(header, inFile, _log)) + 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.); + + auto begin_time = header.begin_time; + auto end_time = header.end_time; + + const auto memory_size = header.memory_size; + const auto descriptors_memory_size = header.descriptors_memory_size; + const auto total_blocks_number = header.total_blocks_number; + total_descriptors_number = header.total_descriptors_number; + + if (cpu_frequency != 0) + { + EASY_CONVERT_TO_NANO(begin_time, cpu_frequency, conversion_factor); + EASY_CONVERT_TO_NANO(end_time, cpu_frequency, conversion_factor); + } + + descriptors.reserve(total_descriptors_number); + //const char* olddata = append_regime ? serialized_descriptors.data() : nullptr; + serialized_descriptors.set(descriptors_memory_size); + //validate_pointers(progress, olddata, serialized_descriptors, descriptors, descriptors.size()); + + uint64_t i = 0; + while (!inFile.eof() && descriptors.size() < total_descriptors_number) + { + uint16_t sz = 0; + inFile.read((char*)&sz, sizeof(sz)); + if (sz == 0) + { + descriptors.push_back(nullptr); + continue; + } + + //if (i + sz > descriptors_memory_size) { + // printf("FILE CORRUPTED\n"); + // return 0; + //} + + char* data = serialized_descriptors[i]; + inFile.read(data, sz); + auto descriptor = reinterpret_cast(data); + descriptors.push_back(descriptor); + + i += sz; + if (!update_progress(progress, static_cast(15 * i / descriptors_memory_size), _log)) { return 0; } + } - uint32_t signature = 0; - inFile.read((char*)&signature, sizeof(uint32_t)); - if (signature != PROFILER_SIGNATURE) + using PerThreadStats = std::unordered_map >; + PerThreadStats parent_statistics, frame_statistics; + IdMap identification_table; + + blocks.reserve(total_blocks_number); + //olddata = append_regime ? serialized_blocks.data() : nullptr; + serialized_blocks.set(memory_size); + //validate_pointers(progress, olddata, serialized_blocks, blocks, blocks.size()); + + i = 0; + uint32_t read_number = 0; + profiler::block_index_t blocks_counter = 0; + std::vector name; + + 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); + + profiler::thread_id_t thread_id = 0; + inFile.read((char*)&thread_id, thread_id_t_size); + if (inFile.eof()) + break; + + auto& root = threaded_trees[thread_id]; + + uint16_t name_size = 0; + inFile.read((char*)&name_size, sizeof(uint16_t)); + if (name_size != 0) { - _log << "Wrong signature " << signature << "\nThis is not EasyProfiler file/stream."; - return 0; + name.resize(name_size); + inFile.read(name.data(), name_size); + root.thread_name = name.data(); } - version = 0; - inFile.read((char*)&version, sizeof(uint32_t)); - if (!isCompatibleVersion(version)) + CsStatsMap per_thread_statistics_cs; + + uint32_t blocks_number_in_thread = 0; + inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(blocks_number_in_thread))); + auto threshold = read_number + blocks_number_in_thread; + while (!inFile.eof() && read_number < threshold) { - _log << "Incompatible version: v" << (version >> 24) << "." << ((version & 0x00ff0000) >> 16) << "." << (version & 0x0000ffff); - return 0; - } + EASY_BLOCK("Read context switch", profiler::colors::Green); - EasyFileHeader header; - header.signature = signature; - header.version = version; + ++read_number; - if (version < EASY_V_200) - { - if (!readHeader_v1(header, inFile, _log)) - return 0; - } - else - { - if (!readHeader_v2(header, inFile, _log)) - return 0; - } - - const uint64_t cpu_frequency = header.cpu_frequency; - const double conversion_factor = (cpu_frequency != 0 ? static_cast(TIME_FACTOR) / static_cast(cpu_frequency) : 1.); - - auto begin_time = header.begin_time; - auto end_time = header.end_time; - - const auto memory_size = header.memory_size; - const auto descriptors_memory_size = header.descriptors_memory_size; - const auto total_blocks_number = header.total_blocks_number; - total_descriptors_number = header.total_descriptors_number; - - if (cpu_frequency != 0) - { - EASY_CONVERT_TO_NANO(begin_time, cpu_frequency, conversion_factor); - EASY_CONVERT_TO_NANO(end_time, cpu_frequency, conversion_factor); - } - - descriptors.reserve(total_descriptors_number); - //const char* olddata = append_regime ? serialized_descriptors.data() : nullptr; - serialized_descriptors.set(descriptors_memory_size); - //validate_pointers(progress, olddata, serialized_descriptors, descriptors, descriptors.size()); - - uint64_t i = 0; - while (!inFile.eof() && descriptors.size() < total_descriptors_number) - { uint16_t sz = 0; inFile.read((char*)&sz, sizeof(sz)); if (sz == 0) { - descriptors.push_back(nullptr); - continue; + _log << "Bad CSwitch block size == 0"; + return 0; } - //if (i + sz > descriptors_memory_size) { - // printf("FILE CORRUPTED\n"); - // return 0; - //} - - char* data = serialized_descriptors[i]; - inFile.read(data, sz); - auto descriptor = reinterpret_cast<::profiler::SerializedBlockDescriptor*>(data); - descriptors.push_back(descriptor); - - i += sz; - if (!update_progress(progress, static_cast(15 * i / descriptors_memory_size), _log)) + if (i + sz > memory_size) { + _log << "File corrupted.\nActual context switches data size > size pointed in file."; return 0; } + + char* data = serialized_blocks[i]; + inFile.read(data, sz); + i += sz; + auto baseData = reinterpret_cast(data); + auto t_begin = reinterpret_cast(data); + auto t_end = t_begin + 1; + + if (cpu_frequency != 0) + { + EASY_CONVERT_TO_NANO(*t_begin, cpu_frequency, conversion_factor); + EASY_CONVERT_TO_NANO(*t_end, cpu_frequency, conversion_factor); + } + + if (*t_end > begin_time) + { + if (*t_begin < begin_time) + *t_begin = begin_time; + + blocks.emplace_back(); + profiler::BlocksTree& tree = blocks.back(); + tree.cs = baseData; + const auto block_index = blocks_counter++; + + root.wait_time += baseData->duration(); + root.sync.emplace_back(block_index); + + if (gather_statistics) + { + EASY_BLOCK("Gather per thread statistics", profiler::colors::Coral); + tree.per_thread_stats = update_statistics(per_thread_statistics_cs, tree, block_index, ~0U, blocks);//, thread_id, blocks); + } + } + + if (!update_progress(progress, 20 + static_cast(70 * i / memory_size), _log)) + { + return 0; // Loading interrupted + } } - using PerThreadStats = ::std::unordered_map<::profiler::thread_id_t, StatsMap, ::estd::hash<::profiler::thread_id_t> >; - PerThreadStats parent_statistics, frame_statistics; - IdMap identification_table; + if (inFile.eof()) + break; - blocks.reserve(total_blocks_number); - //olddata = append_regime ? serialized_blocks.data() : nullptr; - serialized_blocks.set(memory_size); - //validate_pointers(progress, olddata, serialized_blocks, blocks, blocks.size()); + StatsMap per_thread_statistics; - i = 0; - uint32_t read_number = 0; - ::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); - - while (!inFile.eof()) + blocks_number_in_thread = 0; + inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(blocks_number_in_thread))); + threshold = read_number + blocks_number_in_thread; + while (!inFile.eof() && read_number < threshold) { - EASY_BLOCK("Read thread data", ::profiler::colors::DarkGreen); + EASY_BLOCK("Read block", profiler::colors::Green); - ::profiler::thread_id_t thread_id = 0; - inFile.read((char*)&thread_id, thread_id_t_size); - if (inFile.eof()) - break; + ++read_number; - auto& root = threaded_trees[thread_id]; - - uint16_t name_size = 0; - inFile.read((char*)&name_size, sizeof(uint16_t)); - if (name_size != 0) + uint16_t sz = 0; + inFile.read((char*)&sz, sizeof(sz)); + if (sz == 0) { - name.resize(name_size); - inFile.read(name.data(), name_size); - root.thread_name = name.data(); + _log << "Bad block size == 0"; + return 0; } - CsStatsMap per_thread_statistics_cs; - - uint32_t blocks_number_in_thread = 0; - inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(blocks_number_in_thread))); - auto threshold = read_number + blocks_number_in_thread; - while (!inFile.eof() && read_number < threshold) + if (i + sz > memory_size) { - EASY_BLOCK("Read context switch", ::profiler::colors::Green); + _log << "File corrupted.\nActual blocks data size > size pointed in file."; + return 0; + } - ++read_number; + char* data = serialized_blocks[i]; + inFile.read(data, sz); + i += sz; + auto baseData = reinterpret_cast(data); + if (baseData->id() >= total_descriptors_number) + { + _log << "Bad block id == " << baseData->id(); + return 0; + } - uint16_t sz = 0; - inFile.read((char*)&sz, sizeof(sz)); - if (sz == 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(data); + auto t_end = t_begin + 1; + + if (cpu_frequency != 0) + { + EASY_CONVERT_TO_NANO(*t_begin, cpu_frequency, conversion_factor); + EASY_CONVERT_TO_NANO(*t_end, cpu_frequency, conversion_factor); + } + + if (*t_end >= begin_time) + { + if (*t_begin < begin_time) + *t_begin = begin_time; + + blocks.emplace_back(); + profiler::BlocksTree& tree = blocks.back(); + tree.node = baseData; + const auto block_index = blocks_counter++; + + if (*tree.node->name() != 0) { - _log << "Bad CSwitch block size == 0"; - return 0; - } + // If block has runtime name then generate new id for such block. + // Blocks with the same name will have same id. - 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 t_end = t_begin + 1; - - if (cpu_frequency != 0) - { - EASY_CONVERT_TO_NANO(*t_begin, cpu_frequency, conversion_factor); - EASY_CONVERT_TO_NANO(*t_end, cpu_frequency, conversion_factor); - } - - if (*t_end > begin_time) - { - if (*t_begin < begin_time) - *t_begin = begin_time; - - blocks.emplace_back(); - ::profiler::BlocksTree& tree = blocks.back(); - tree.cs = baseData; - const auto block_index = blocks_counter++; - - root.wait_time += baseData->duration(); - root.sync.emplace_back(block_index); - - if (gather_statistics) + IdMap::key_type key(tree.node->name()); + auto it = identification_table.find(key); + if (it != identification_table.end()) { - 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); + // There is already block with such name, use it's id + baseData->setId(it->second); + } + else + { + // There were no blocks with such name, generate new id and save it in the table for further usage. + auto id = static_cast(descriptors.size()); + identification_table.emplace(key, id); + if (descriptors.capacity() == descriptors.size()) + descriptors.reserve((descriptors.size() * 3) >> 1); + descriptors.push_back(descriptors[baseData->id()]); + baseData->setId(id); } } - if (!update_progress(progress, 20 + static_cast(70 * i / memory_size), _log)) + if (!root.children.empty()) { - return 0; // Loading interrupted - } - } - - if (inFile.eof()) - break; - - StatsMap per_thread_statistics; - - blocks_number_in_thread = 0; - inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(blocks_number_in_thread))); - threshold = read_number + blocks_number_in_thread; - while (!inFile.eof() && read_number < threshold) - { - EASY_BLOCK("Read block", ::profiler::colors::Green); - - ++read_number; - - uint16_t sz = 0; - inFile.read((char*)&sz, sizeof(sz)); - if (sz == 0) - { - _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; - - if (cpu_frequency != 0) - { - EASY_CONVERT_TO_NANO(*t_begin, cpu_frequency, conversion_factor); - EASY_CONVERT_TO_NANO(*t_end, cpu_frequency, conversion_factor); - } - - if (*t_end >= begin_time) - { - if (*t_begin < begin_time) - *t_begin = begin_time; - - blocks.emplace_back(); - ::profiler::BlocksTree& tree = blocks.back(); - tree.node = baseData; - const auto block_index = blocks_counter++; - - if (*tree.node->name() != 0) + auto& back = blocks[root.children.back()]; + auto t1 = back.node->end(); + auto mt0 = tree.node->begin(); + if (mt0 < t1)//parent - starts earlier than last ends { - // If block has runtime name then generate new id for such block. - // Blocks with the same name will have same id. + //auto lower = std::lower_bound(root.children.begin(), root.children.end(), tree); + /**/ + EASY_BLOCK("Find children", profiler::colors::Blue); + auto rlower1 = ++root.children.rbegin(); + for (; rlower1 != root.children.rend() && mt0 <= blocks[*rlower1].node->begin(); ++rlower1); + auto lower = rlower1.base(); + std::move(lower, root.children.end(), std::back_inserter(tree.children)); - IdMap::key_type key(tree.node->name()); - auto it = identification_table.find(key); - if (it != identification_table.end()) + root.children.erase(lower, root.children.end()); + EASY_END_BLOCK; + + if (gather_statistics) { - // There is already block with such name, use it's id - baseData->setId(it->second); + EASY_BLOCK("Gather statistic within parent", profiler::colors::Magenta); + auto& per_parent_statistics = parent_statistics[thread_id]; + per_parent_statistics.clear(); + + //per_parent_statistics.reserve(tree.children.size()); // this gives slow-down on Windows + //per_parent_statistics.reserve(tree.children.size() * 2); // this gives no speed-up on Windows + // TODO: check this behavior on Linux + + for (auto child_block_index : tree.children) + { + auto& child = blocks[child_block_index]; + child.per_parent_stats = update_statistics(per_parent_statistics, child, child_block_index, block_index, blocks); + if (tree.depth < child.depth) + tree.depth = child.depth; + } } else { - // There were no blocks with such name, generate new id and save it in the table for further usage. - auto id = static_cast<::profiler::block_id_t>(descriptors.size()); - identification_table.emplace(key, id); - if (descriptors.capacity() == descriptors.size()) - descriptors.reserve((descriptors.size() * 3) >> 1); - descriptors.push_back(descriptors[baseData->id()]); - baseData->setId(id); - } - } - - if (!root.children.empty()) - { - auto& back = blocks[root.children.back()]; - auto t1 = back.node->end(); - auto mt0 = tree.node->begin(); - if (mt0 < t1)//parent - starts earlier than last ends - { - //auto lower = ::std::lower_bound(root.children.begin(), root.children.end(), tree); - /**/ - EASY_BLOCK("Find children", ::profiler::colors::Blue); - auto rlower1 = ++root.children.rbegin(); - for (; rlower1 != root.children.rend() && mt0 <= blocks[*rlower1].node->begin(); ++rlower1); - auto lower = rlower1.base(); - ::std::move(lower, root.children.end(), ::std::back_inserter(tree.children)); - - root.children.erase(lower, root.children.end()); - EASY_END_BLOCK; - - if (gather_statistics) + for (auto child_block_index : tree.children) { - EASY_BLOCK("Gather statistic within parent", ::profiler::colors::Magenta); - auto& per_parent_statistics = parent_statistics[thread_id]; - per_parent_statistics.clear(); - - //per_parent_statistics.reserve(tree.children.size()); // this gives slow-down on Windows - //per_parent_statistics.reserve(tree.children.size() * 2); // this gives no speed-up on Windows - // TODO: check this behavior on Linux - - for (auto child_block_index : tree.children) - { - auto& child = blocks[child_block_index]; - child.per_parent_stats = update_statistics(per_parent_statistics, child, child_block_index, block_index, blocks); - if (tree.depth < child.depth) - tree.depth = child.depth; - } + const auto& child = blocks[child_block_index]; + if (tree.depth < child.depth) + tree.depth = child.depth; } + } + + if (tree.depth == 254) + { + // 254 because we need 1 additional level for root (thread). + // In other words: real stack depth = 1 root block + 254 children + + if (*tree.node->name() != 0) + _log << "Stack depth exceeded value of 254\nfor block \"" << desc->name() << "\""; else - { - for (auto child_block_index : tree.children) - { - const auto& child = blocks[child_block_index]; - if (tree.depth < child.depth) - tree.depth = child.depth; - } - } + _log << "Stack depth exceeded value of 254\nfor block \"" << desc->name() << "\"\nfrom file \"" << desc->file() << "\":" << desc->line(); - if (tree.depth == 254) - { - // 254 because we need 1 additional level for root (thread). - // In other words: real stack depth = 1 root block + 254 children - - if (*tree.node->name() != 0) - _log << "Stack depth exceeded value of 254\nfor block \"" << desc->name() << "\""; - else - _log << "Stack depth exceeded value of 254\nfor block \"" << desc->name() << "\"\nfrom file \"" << desc->file() << "\":" << desc->line(); - - return 0; - } - - ++tree.depth; + return 0; } - } - ++root.blocks_number; - 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); - tree.per_thread_stats = update_statistics(per_thread_statistics, tree, block_index, ~0U, blocks);//, thread_id, blocks); + ++tree.depth; } } - if (!update_progress(progress, 20 + static_cast(70 * i / memory_size), _log)) + ++root.blocks_number; + root.children.emplace_back(block_index);// std::move(tree)); + if (desc->type() != profiler::BlockType::Block) + root.events.emplace_back(block_index); + + + if (gather_statistics) { - return 0; // Loading interrupted + 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); } } - } - if (!update_progress(progress, 90, _log)) - { - return 0; // Loading interrupted - } - - EASY_BLOCK("Gather statistics for roots", ::profiler::colors::Purple); - if (gather_statistics) - { - ::std::vector<::std::thread> statistics_threads; - statistics_threads.reserve(threaded_trees.size()); - - for (auto& it : threaded_trees) + if (!update_progress(progress, 20 + static_cast(70 * i / memory_size), _log)) { - auto& root = it.second; - root.thread_id = it.first; - //root.tree.shrink_to_fit(); - - auto& per_frame_statistics = frame_statistics[root.thread_id]; - auto& per_parent_statistics = parent_statistics[it.first]; - per_parent_statistics.clear(); - - statistics_threads.emplace_back(::std::thread([&] (::profiler::BlocksTreeRoot& _root) - { - //::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; - for (auto child_index : _root.children) - { - auto& frame = blocks[child_index]; - - 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); - - per_frame_statistics.clear(); - update_statistics_recursive(per_frame_statistics, frame, child_index, child_index, blocks); - - if (cs_index < _root.sync.size()) - { - CsStatsMap frame_stats_cs; - do { - - auto j = _root.sync[cs_index]; - auto& cs = blocks[j]; - if (cs.node->end() < frame.node->begin()) - continue; - if (cs.node->begin() > frame.node->end()) - break; - cs.per_frame_stats = update_statistics(frame_stats_cs, cs, cs_index, child_index, blocks); - - } while (++cs_index < _root.sync.size()); - } - - if (_root.depth < frame.depth) - _root.depth = frame.depth; - - _root.profiled_time += frame.node->duration(); - } - - ++_root.depth; - }, ::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); + return 0; // Loading interrupted } } - else - { - int j = 0, n = static_cast(threaded_trees.size()); - for (auto& it : threaded_trees) - { - auto& root = it.second; - root.thread_id = it.first; + } - //::std::sort(root.sync.begin(), root.sync.end(), [&blocks](::profiler::block_index_t left, ::profiler::block_index_t right) + if (!update_progress(progress, 90, _log)) + { + return 0; // Loading interrupted + } + + EASY_BLOCK("Gather statistics for roots", profiler::colors::Purple); + if (gather_statistics) + { + std::vector statistics_threads; + statistics_threads.reserve(threaded_trees.size()); + + for (auto& it : threaded_trees) + { + auto& root = it.second; + root.thread_id = it.first; + //root.tree.shrink_to_fit(); + + auto& per_frame_statistics = frame_statistics[root.thread_id]; + auto& per_parent_statistics = parent_statistics[it.first]; + per_parent_statistics.clear(); + + statistics_threads.emplace_back(std::thread([&] (profiler::BlocksTreeRoot& _root) + { + //std::sort(root.sync.begin(), root.sync.end(), [&blocks](profiler::block_index_t left, profiler::block_index_t right) //{ // return blocks[left].node->begin() < blocks[right].node->begin(); //}); - //root.tree.shrink_to_fit(); - for (auto child_block_index : root.children) + profiler::block_index_t cs_index = 0; + for (auto child_index : _root.children) { - auto& frame = blocks[child_block_index]; + auto& frame = blocks[child_index]; - if (descriptors[frame.node->id()]->type() == ::profiler::BlockType::Block) - ++root.frames_number; + if (descriptors[frame.node->id()]->type() == profiler::BlockType::Block) + ++_root.frames_number; - if (root.depth < frame.depth) - root.depth = frame.depth; + frame.per_parent_stats = update_statistics(per_parent_statistics, frame, child_index, ~0U, blocks);//, root.thread_id, blocks); - root.profiled_time += frame.node->duration(); + per_frame_statistics.clear(); + update_statistics_recursive(per_frame_statistics, frame, child_index, child_index, blocks); + + if (cs_index < _root.sync.size()) + { + CsStatsMap frame_stats_cs; + do { + + auto j = _root.sync[cs_index]; + auto& cs = blocks[j]; + if (cs.node->end() < frame.node->begin()) + continue; + if (cs.node->begin() > frame.node->end()) + break; + cs.per_frame_stats = update_statistics(frame_stats_cs, cs, cs_index, child_index, blocks); + + } while (++cs_index < _root.sync.size()); + } + + if (_root.depth < frame.depth) + _root.depth = frame.depth; + + _root.profiled_time += frame.node->duration(); } - ++root.depth; - - progress.store(90 + (10 * ++j) / n, ::std::memory_order_release); - } + ++_root.depth; + }, std::ref(root))); } - // No need to delete BlockStatistics instances - they will be deleted inside BlocksTree destructors - progress.store(100, ::std::memory_order_release); - return blocks_counter; + 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); + } } - - ////////////////////////////////////////////////////////////////////////// - - PROFILER_API bool readDescriptionsFromStream(::std::atomic& progress, ::std::stringstream& inFile, - ::profiler::SerializedData& serialized_descriptors, - ::profiler::descriptors_list_t& descriptors, - ::std::stringstream& _log) + else { - EASY_FUNCTION(::profiler::colors::Cyan); - - progress.store(0); - - uint32_t signature = 0; - inFile.read((char*)&signature, sizeof(uint32_t)); - if (signature != PROFILER_SIGNATURE) + int j = 0, n = static_cast(threaded_trees.size()); + for (auto& it : threaded_trees) { - _log << "Wrong file signature.\nThis is not EasyProfiler file/stream."; - return false; - } + auto& root = it.second; + root.thread_id = it.first; - 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; - } + //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(); + //}); - 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; - serialized_descriptors.set(descriptors_memory_size); - //validate_pointers(progress, olddata, serialized_descriptors, descriptors, descriptors.size()); - - uint64_t i = 0; - while (!inFile.eof() && descriptors.size() < total_descriptors_number) - { - uint16_t sz = 0; - inFile.read((char*)&sz, sizeof(sz)); - if (sz == 0) + //root.tree.shrink_to_fit(); + for (auto child_block_index : root.children) { - descriptors.push_back(nullptr); - continue; + auto& frame = blocks[child_block_index]; + + if (descriptors[frame.node->id()]->type() == profiler::BlockType::Block) + ++root.frames_number; + + if (root.depth < frame.depth) + root.depth = frame.depth; + + root.profiled_time += frame.node->duration(); } - //if (i + sz > descriptors_memory_size) { - // printf("FILE CORRUPTED\n"); - // return 0; - //} + ++root.depth; - char* data = serialized_descriptors[i]; - inFile.read(data, sz); - auto descriptor = reinterpret_cast<::profiler::SerializedBlockDescriptor*>(data); - descriptors.push_back(descriptor); - - i += sz; - if (!update_progress(progress, static_cast(100 * i / descriptors_memory_size), _log)) - { - return false; // Loading interrupted - } + progress.store(90 + (10 * ++j) / n, std::memory_order_release); } + } + // No need to delete BlockStatistics instances - they will be deleted inside BlocksTree destructors - return !descriptors.empty(); + progress.store(100, std::memory_order_release); + return blocks_counter; +} + +////////////////////////////////////////////////////////////////////////// + +extern "C" 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); + + progress.store(0); + + uint32_t signature = 0; + inFile.read((char*)&signature, sizeof(uint32_t)); + if (signature != PROFILER_SIGNATURE) + { + _log << "Wrong file 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; + serialized_descriptors.set(descriptors_memory_size); + //validate_pointers(progress, olddata, serialized_descriptors, descriptors, descriptors.size()); + + uint64_t i = 0; + while (!inFile.eof() && descriptors.size() < total_descriptors_number) + { + uint16_t sz = 0; + inFile.read((char*)&sz, sizeof(sz)); + if (sz == 0) + { + descriptors.push_back(nullptr); + continue; + } + + //if (i + sz > descriptors_memory_size) { + // printf("FILE CORRUPTED\n"); + // return 0; + //} + + char* data = serialized_descriptors[i]; + inFile.read(data, sz); + auto descriptor = reinterpret_cast(data); + descriptors.push_back(descriptor); + + i += sz; + if (!update_progress(progress, static_cast(100 * i / descriptors_memory_size), _log)) + { + return false; // Loading interrupted + } + } + + return !descriptors.empty(); } +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +struct BlocksRange +{ + profiler::block_index_t begin; + profiler::block_index_t end; + + BlocksRange(profiler::block_index_t size = 0) + : begin(size), end(size) + { + } + + BlocksRange(profiler::block_index_t beginIndex, profiler::block_index_t endIndex) + : begin(beginIndex), end(endIndex) + { + } +}; + +struct BlocksMemoryAndCount +{ + uint64_t usedMemorySize = 0; // memory size used by profiler blocks + profiler::block_index_t blocksCount = 0; + + BlocksMemoryAndCount() = default; + + BlocksMemoryAndCount& operator += (const BlocksMemoryAndCount& another) + { + usedMemorySize += another.usedMemorySize; + blocksCount += another.blocksCount; + return *this; + } +}; + +struct BlocksAndCSwitchesRange +{ + BlocksMemoryAndCount blocksMemoryAndCount; + BlocksMemoryAndCount cswitchesMemoryAndCount; + BlocksRange blocks; + BlocksRange cswitches; +}; + +template +inline void write(std::ostream& _stream, const char* _data, T _size) +{ + _stream.write(_data, _size); +} + +template +inline void write(std::ostream& _stream, const T& _data) +{ + _stream.write((const char*)&_data, sizeof(T)); +} + +BlocksRange findRange(const profiler::BlocksTree::children_t& children, profiler::timestamp_t beginTime, profiler::timestamp_t endTime, + const profiler::block_getter_fn& getter) +{ + const auto size = static_cast(children.size()); + BlocksRange range(size); + + auto first_it = std::lower_bound(children.begin(), children.end(), beginTime, [&](profiler::block_index_t element, profiler::timestamp_t value) + { + return getter(element).node->end() < value; + }); + + for (auto it = first_it; it != children.end(); ++it) + { + const auto& child = getter(*it); + if (child.node->begin() >= beginTime || child.node->end() > beginTime) + break; + } + + if (first_it != children.end() && getter(*first_it).node->begin() <= endTime) + { + auto last_it = std::lower_bound(children.begin(), children.end(), endTime, [&](profiler::block_index_t element, profiler::timestamp_t value) + { + return getter(element).node->begin() <= value; + }); + + if (last_it != children.end() && getter(*last_it).node->end() >= beginTime) + { + const auto begin = static_cast(std::distance(children.begin(), first_it)); + const auto end = static_cast(std::distance(children.begin(), last_it)); + + if (begin <= end) + { + range.begin = begin; + range.end = end; + } + } + } + + return range; +} + +BlocksMemoryAndCount calculateUsedMemoryAndBlocksCount(const profiler::BlocksTree::children_t& children, + const BlocksRange& range, + const profiler::block_getter_fn& getter, + const profiler::descriptors_list_t& descriptors, + bool contextSwitches) +{ + BlocksMemoryAndCount memoryAndCount; + + if (!contextSwitches) + { + for (auto i = range.begin; i < range.end; ++i) + { + const auto& child = getter(children[i]); + + // Calculate self memory consumption + const auto& desc = *descriptors[child.node->id()]; + uint64_t usedMemorySize = 0; + + if (desc.type() == profiler::BlockType::Value) + usedMemorySize = sizeof(profiler::ArbitraryValue) + child.value->data_size(); + else + usedMemorySize = sizeof(profiler::SerializedBlock) + strlen(child.node->name()) + 1; + + // Calculate children memory consumption + const BlocksRange childRange(0, static_cast(child.children.size())); + const auto childrenMemoryAndCount = calculateUsedMemoryAndBlocksCount(child.children, childRange, + getter, descriptors, + false); + + // Accumulate memory and count + memoryAndCount += childrenMemoryAndCount; + memoryAndCount.usedMemorySize += usedMemorySize; + ++memoryAndCount.blocksCount; + } + } + else + { + for (auto i = range.begin; i < range.end; ++i) + { + const auto& child = getter(children[i]); + const uint64_t usedMemorySize = sizeof(profiler::SerializedCSwitch) + strlen(child.cs->name()) + 1; + memoryAndCount.usedMemorySize += usedMemorySize; + ++memoryAndCount.blocksCount; + } + } + + return memoryAndCount; +} + +void serializeBlocks(std::ostream& output, std::vector& buffer, const profiler::BlocksTree::children_t& children, + const BlocksRange& range, const profiler::block_getter_fn& getter, + const profiler::descriptors_list_t& descriptors) +{ + for (auto i = range.begin; i < range.end; ++i) + { + const auto& child = getter(children[i]); + + // Serialize children + const BlocksRange childRange(0, static_cast(child.children.size())); + serializeBlocks(output, buffer, child.children, childRange, getter, descriptors); + + // Serialize self + const auto& desc = *descriptors[child.node->id()]; + uint16_t usedMemorySize = 0; + + if (desc.type() == profiler::BlockType::Value) + { + usedMemorySize = static_cast(sizeof(profiler::ArbitraryValue)) + child.value->data_size(); + buffer.resize(usedMemorySize + sizeof(uint16_t)); + unaligned_store16(buffer.data(), usedMemorySize); + memcpy(buffer.data() + sizeof(uint16_t), child.value, static_cast(usedMemorySize)); + } + else + { + usedMemorySize = static_cast(sizeof(profiler::SerializedBlock) + + strlen(child.node->name()) + 1); + + buffer.resize(usedMemorySize + sizeof(uint16_t)); + unaligned_store16(buffer.data(), usedMemorySize); + memcpy(buffer.data() + sizeof(uint16_t), child.node, static_cast(usedMemorySize)); + + if (child.node->id() != desc.id()) + { + // This block id is dynamic. Restore it's value like it was before in the input .prof file + auto block = reinterpret_cast(buffer.data() + sizeof(uint16_t)); + block->setId(desc.id()); + } + } + + write(output, buffer.data(), buffer.size()); + } +} + +void serializeContextSwitches(std::ostream& output, std::vector& buffer, const profiler::BlocksTree::children_t& children, + const BlocksRange& range, const profiler::block_getter_fn& getter) +{ + for (auto i = range.begin; i < range.end; ++i) + { + const auto& child = getter(children[i]); + + const auto usedMemorySize = static_cast( + sizeof(profiler::SerializedCSwitch) + strlen(child.cs->name()) + 1); + + buffer.resize(usedMemorySize + sizeof(uint16_t)); + unaligned_store16(buffer.data(), usedMemorySize); + memcpy(buffer.data() + sizeof(uint16_t), child.cs, static_cast(usedMemorySize)); + + write(output, buffer.data(), buffer.size()); + } +} + +void serializeDescriptors(std::ostream& output, std::vector& buffer, + const profiler::descriptors_list_t& descriptors, + profiler::block_id_t descriptors_count) +{ + const size_t size = std::min(descriptors.size(), static_cast(descriptors_count)); + for (size_t i = 0; i < size; ++i) + { + const auto& desc = *descriptors[i]; + if (desc.id() != i) + break; + + const auto usedMemorySize = static_cast(sizeof(profiler::SerializedBlockDescriptor) + + strlen(desc.name()) + strlen(desc.file()) + 2); + + buffer.resize(usedMemorySize + sizeof(uint16_t)); + unaligned_store16(buffer.data(), usedMemorySize); + memcpy(buffer.data() + sizeof(uint16_t), &desc, static_cast(usedMemorySize)); + + write(output, buffer.data(), buffer.size()); + } +} + +extern "C" PROFILER_API profiler::block_index_t writeTreesToFile(std::atomic& progress, const char* filename, + const profiler::SerializedData& serialized_descriptors, + const profiler::descriptors_list_t& 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, descriptors_count, trees, + std::move(block_getter), begin_time, end_time, pid, log); + + return result; +} + +////////////////////////////////////////////////////////////////////////// + +extern "C" PROFILER_API profiler::block_index_t writeTreesToStream(std::atomic& progress, std::ostream& str, + const profiler::SerializedData& serialized_descriptors, + const profiler::descriptors_list_t& 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; + } + + BlocksMemoryAndCount total; + + using ranges_t = std::unordered_map >; + ranges_t block_ranges; + + // Calculate block ranges and used memory (for serialization) + profiler::timestamp_t beginTime = begin_time, endTime = end_time; + size_t i = 0; + for (const auto& kv : trees) + { + const auto id = kv.first; + const auto& tree = kv.second; + + BlocksAndCSwitchesRange range; + + range.blocks = findRange(tree.children, begin_time, end_time, block_getter); + range.cswitches = findRange(tree.sync, begin_time, end_time, block_getter); + + range.blocksMemoryAndCount = calculateUsedMemoryAndBlocksCount(tree.children, range.blocks, block_getter, + descriptors, false); + total += range.blocksMemoryAndCount; + + if (range.blocksMemoryAndCount.blocksCount != 0) + { + beginTime = std::min(beginTime, block_getter(tree.children[range.blocks.begin]).node->begin()); + endTime = std::max(endTime, block_getter(tree.children[range.blocks.end - 1]).node->end()); + } + + range.cswitchesMemoryAndCount = calculateUsedMemoryAndBlocksCount(tree.sync, range.cswitches, block_getter, + descriptors, true); + total += range.cswitchesMemoryAndCount; + + if (range.cswitchesMemoryAndCount.blocksCount != 0) + { + beginTime = std::min(beginTime, block_getter(tree.children[range.cswitches.begin]).cs->begin()); + endTime = std::max(endTime, block_getter(tree.children[range.cswitches.end - 1]).cs->end()); + } + + block_ranges[id] = range; + + if (!update_progress_write(progress, 15 / static_cast(trees.size() - i), log)) + return 0; + + ++i; + } + + if (total.blocksCount == 0) + { + log << "Nothing to save"; + return 0; + } + + const uint64_t usedMemorySizeDescriptors = serialized_descriptors.size() + descriptors_count * sizeof(uint16_t); + + // Write data to stream + write(str, PROFILER_SIGNATURE); + write(str, EASY_CURRENT_VERSION); + write(str, pid); + + // write 0 because we do not need to convert time from ticks to nanoseconds (it's already converted) + write(str, 0LL); // CPU frequency + + write(str, beginTime); + write(str, endTime); + + write(str, total.usedMemorySize); + write(str, usedMemorySizeDescriptors); + write(str, total.blocksCount); + write(str, descriptors_count); + + std::vector buffer; + + // Serialize all descriptors + serializeDescriptors(str, buffer, descriptors, descriptors_count); + + // Serialize all blocks + i = 0; + for (const auto& kv : trees) + { + const auto id = kv.first; + const auto& tree = kv.second; + const auto& range = block_ranges.at(id); + + const auto nameSize = static_cast(tree.thread_name.size() + 1); + write(str, id); + write(str, nameSize); + write(str, tree.name(), nameSize); + + // Serialize context switches + write(str, range.cswitchesMemoryAndCount.blocksCount); + if (range.cswitchesMemoryAndCount.blocksCount != 0) + serializeContextSwitches(str, buffer, tree.sync, range.cswitches, block_getter); + + // Serialize blocks + write(str, range.blocksMemoryAndCount.blocksCount); + if (range.blocksMemoryAndCount.blocksCount != 0) + serializeBlocks(str, buffer, tree.children, range.blocks, block_getter, descriptors); + + if (!update_progress_write(progress, 40 + 60 / static_cast(trees.size() - i), log)) + return 0; + } + + return total.blocksCount; +} + +////////////////////////////////////////////////////////////////////////// + #undef EASY_CONVERT_TO_NANO #ifdef EASY_USE_FLOATING_POINT_CONVERSION diff --git a/profiler_gui/blocks_graphics_view.cpp b/profiler_gui/blocks_graphics_view.cpp index c89037f..d491bab 100644 --- a/profiler_gui/blocks_graphics_view.cpp +++ b/profiler_gui/blocks_graphics_view.cpp @@ -407,6 +407,8 @@ void BlocksGraphicsView::clear() EASY_GLOBALS.selected_thread = 0; emit EASY_GLOBALS.events.selectedThreadChanged(0); + + emit EASY_GLOBALS.events.rulerVisible(false); } void BlocksGraphicsView::notifySceneSizeChange() @@ -438,7 +440,10 @@ void BlocksGraphicsView::notifyVisibleRegionPosChange() void BlocksGraphicsView::notifyVisibleRegionPosChange(qreal _pos) { - m_offset = estd::clamp(0., _pos, m_sceneWidth - m_visibleRegionWidth); + if (m_sceneWidth < m_visibleRegionWidth) + m_offset = 0; + else + m_offset = estd::clamp(0., _pos, m_sceneWidth - m_visibleRegionWidth); notifyVisibleRegionPosChange(); } @@ -614,6 +619,21 @@ const BlocksGraphicsView::Items &BlocksGraphicsView::getItems() const return m_items; } +bool BlocksGraphicsView::getSelectionRegionForSaving(profiler::timestamp_t& _beginTime, profiler::timestamp_t& _endTime) const +{ + if (m_bEmpty) + return false; + + if (!m_selectionItem->isVisible() && !m_rulerItem->isVisible()) + return false; + + decltype(m_selectionItem) ruler = m_selectionItem->isVisible() ? m_selectionItem : m_rulerItem; + _beginTime = m_beginTime + position2time(ruler->left()); + _endTime = m_beginTime + position2time(ruler->right()); + + 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()) @@ -898,7 +918,10 @@ void BlocksGraphicsView::onWheel(qreal _scenePos, int _wheelDelta) notifyVisibleRegionSizeChange(); // Calculate new offset to simulate QGraphicsView::AnchorUnderMouse scaling behavior - m_offset = clamp(0., initialPosition - _scenePos / m_scale, m_sceneWidth - m_visibleRegionWidth); + if (m_sceneWidth < m_visibleRegionWidth) + m_offset = 0; + else + m_offset = clamp(0., initialPosition - _scenePos / m_scale, m_sceneWidth - m_visibleRegionWidth); // Update slider position profiler_gui::BoolFlagGuard guard(m_bUpdatingRect, true); // To be sure that updateVisibleSceneRect will not be called by scrollbar change @@ -955,6 +978,7 @@ void BlocksGraphicsView::mousePressEvent(QMouseEvent* _event) m_selectionItem->setLeftRight(mouseX, mouseX); m_selectionItem->hide(); m_pScrollbar->hideSelectionIndicator(); + emit EASY_GLOBALS.events.rulerVisible(m_rulerItem->isVisible()); } } @@ -984,6 +1008,7 @@ void BlocksGraphicsView::mouseDoubleClickEvent(QMouseEvent* _event) m_rulerItem->setLeftRight(mouseX, mouseX); m_rulerItem->hide(); emit sceneUpdated(); + emit EASY_GLOBALS.events.rulerVisible(m_selectionItem->isVisible()); } _event->accept(); @@ -1010,6 +1035,7 @@ void BlocksGraphicsView::mouseReleaseEvent(QMouseEvent* _event) { m_selectionItem->hide(); m_pScrollbar->hideSelectionIndicator(); + emit EASY_GLOBALS.events.rulerVisible(m_rulerItem->isVisible()); } if (!m_selectedBlocks.empty()) @@ -1046,6 +1072,7 @@ void BlocksGraphicsView::mouseReleaseEvent(QMouseEvent* _event) { chronoHidden = true; m_rulerItem->hide(); + emit EASY_GLOBALS.events.rulerVisible(m_selectionItem->isVisible()); } else if (m_selectionItem->isVisible() && m_selectionItem->hoverIndicator()) { @@ -1191,6 +1218,9 @@ void BlocksGraphicsView::addSelectionToHierarchy() void BlocksGraphicsView::onZoomSelection() { + if (m_selectionItem->width() < 1e-6) + return; + auto deltaScale = m_visibleRegionWidth / m_selectionItem->width(); if (fabs(deltaScale - 1) < 1e-6) @@ -1229,6 +1259,9 @@ void BlocksGraphicsView::onInspectCurrentView(bool _strict) m_selectionItem->show(); m_pScrollbar->setSelectionPos(m_selectionItem->left(), m_selectionItem->right()); m_pScrollbar->showSelectionIndicator(); + + emit EASY_GLOBALS.events.rulerVisible(true); + addSelectionToHierarchy(); } else @@ -1281,6 +1314,7 @@ bool BlocksGraphicsView::moveChrono(GraphicsRulerItem* _chronometerItem, qreal _ if (!_chronometerItem->isVisible() && _chronometerItem->width() > 1e-6) { _chronometerItem->show(); + emit EASY_GLOBALS.events.rulerVisible(true); return true; } 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/globals_qobjects.h b/profiler_gui/globals_qobjects.h index 89dfb70..fbaa97b 100644 --- a/profiler_gui/globals_qobjects.h +++ b/profiler_gui/globals_qobjects.h @@ -93,6 +93,7 @@ namespace profiler_gui { void hexThreadIdChanged(); void refreshRequired(); void blocksTreeModeChanged(); + void rulerVisible(bool); void sceneCleared(); void sceneSizeChanged(qreal left, qreal right); diff --git a/profiler_gui/images/attribution.txt b/profiler_gui/images/attribution.txt index 0d0a6cf..e39b0a0 100644 --- a/profiler_gui/images/attribution.txt +++ b/profiler_gui/images/attribution.txt @@ -42,6 +42,7 @@ default/arrow-up-hover.svg - Icon made by Freepik from www.flaticon.com default/arrow-up-disabled.svg - Icon made by Freepik from www.flaticon.com default/arrow-left.svg - Icon made by Freepik from www.flaticon.com default/arrow-right.svg - Icon made by Freepik from www.flaticon.com +default/crop.svg - Icon made by Freepik from www.flaticon.com default/yx.svg - Icon made by Freepik from www.flaticon.com default/svg2.svg - Icon made by Freepik from www.flaticon.com default/svg3.svg - Icon made by Freepik from www.flaticon.com diff --git a/profiler_gui/images/default/crop.svg b/profiler_gui/images/default/crop.svg new file mode 100644 index 0000000..d4162e0 --- /dev/null +++ b/profiler_gui/images/default/crop.svg @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/profiler_gui/main_window.cpp b/profiler_gui/main_window.cpp index fd60508..231936c 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,12 @@ MainWindow::MainWindow() : Parent(), m_theme("default"), m_lastAddress("localhos static_cast(m_graphicsView->widget())->view()->inspectCurrentView(true); }); + action = toolbar->addAction(QIcon(imagePath("crop")), "Snapshot"); + action->setToolTip("Take a snapshot.\nSave selected area to\nseparate .prof file."); + action->setEnabled(false); + connect(action, &QAction::triggered, this, &This::onSnapshotClicked); + connect(&EASY_GLOBALS.events, &profiler_gui::GlobalSignals::rulerVisible, action, &QAction::setEnabled); + toolbar->addSeparator(); auto menu = new QMenu("Settings", this); menu->setToolTipsVisible(true); @@ -547,8 +553,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 +562,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 +571,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 +680,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 +795,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 +969,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 +1124,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 +1188,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 +1386,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 +1407,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 +1536,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 +1566,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 +1845,162 @@ 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() +{ + const auto errorMessage = m_reader.getError(); + if (!errorMessage.isEmpty()) + { + QMessageBox::warning(this, "Warning", QString("Cannot save profiled blocks.\n\nReason:\n%1") + .arg(errorMessage), QMessageBox::Close); + } +} + 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 +2033,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 +2067,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 +2088,7 @@ void FileReader::load(std::stringstream& _stream) { interrupt(); + m_jobType = JobType::Loading; m_isFile = false; m_filename.clear(); @@ -2038,19 +2102,72 @@ 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, + const profiler::descriptors_list_t& _descriptors, 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 descriptors = std::ref(_descriptors); + auto trees = std::ref(_trees); + + m_thread = std::thread([=] (profiler::block_getter_fn getter) + { + const QString tmpFile = m_filename + ".tmp"; + + const auto result = writeTreesToFile(m_progress, tmpFile.toStdString().c_str(), serializedDescriptors, + descriptors, descriptors_count, trees, getter, _beginTime, _endTime, + _pid, m_errorMessage); + + if (result == 0 || !m_errorMessage.str().empty()) + { + // Remove temporary file in case of error + QFile::remove(tmpFile); + } + else + { + // Remove old file if exists + { + QFile out(m_filename); + if (out.exists()) + out.remove(); + } + + QFile::rename(tmpFile, m_filename); + } + + 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 +2181,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 +2247,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 +2263,37 @@ void MainWindow::onFrameTimeChanged() ////////////////////////////////////////////////////////////////////////// +void MainWindow::onSnapshotClicked(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, EASY_GLOBALS.descriptors, + m_descriptorsNumberInFile, EASY_GLOBALS.profiler_blocks, easyBlocksTree, EASY_GLOBALS.pid); +} + +////////////////////////////////////////////////////////////////////////// + void MainWindow::onConnectClicked(bool) { if (EASY_GLOBALS.connected) @@ -2428,7 +2580,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 +2765,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 +2777,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 +2972,7 @@ void SocketListener::listenCapture() if (bytes > 0) { - auto message = reinterpret_cast(buf); + auto message = reinterpret_cast(buf); if (!message->isEasyNetMessage()) continue; @@ -2978,7 +3130,7 @@ void SocketListener::listenDescription() if (bytes > 0) { - auto message = reinterpret_cast(buf); + auto message = reinterpret_cast(buf); if (!message->isEasyNetMessage()) continue; @@ -3069,7 +3221,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 +3260,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..7330eca 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,18 @@ 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, const profiler::descriptors_list_t& _descriptors, + 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 +258,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 +347,7 @@ protected slots: void onEventTracingEnableChange(bool _checked); void onFrameTimeEditFinish(); void onFrameTimeChanged(); + void onSnapshotClicked(bool); void onBlockStatusChange(profiler::block_id_t _id, profiler::EasyBlockStatus _status); @@ -339,6 +361,10 @@ private: // Private non-virtual methods + void closeProgressDialogAndClearReader(); + void onLoadingFinish(profiler::block_index_t& _nblocks); + void onSavingFinish(); + void configureSizes(); void clear(); diff --git a/profiler_gui/resources.qrc b/profiler_gui/resources.qrc index be73612..78b03c7 100644 --- a/profiler_gui/resources.qrc +++ b/profiler_gui/resources.qrc @@ -7,6 +7,7 @@ images/default/binoculars.svg + images/default/crop.svg images/default/off.svg images/default/open-folder2.svg images/default/reload.svg 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();