/** Lightweight profiler library for c++ Copyright(C) 2016 Sergey Yagovtsev, Victor Zarubkin This program is free software : you can redistribute it and / or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program.If not, see . **/ #ifndef PROFILER_READER____H #define PROFILER_READER____H ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #include #include #include #include "profiler/profiler.h" #include "profiler/serialized_block.h" ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// namespace profiler { typedef uint32_t calls_number_t; typedef uint32_t block_index_t; #pragma pack(push, 1) struct BlockStatistics final { ::profiler::timestamp_t total_duration; ///< Summary duration of all block calls ::profiler::timestamp_t min_duration; ///< Cached block->duration() value. TODO: Remove this if memory consumption will be too high ::profiler::timestamp_t max_duration; ///< Cached block->duration() value. TODO: Remove this if memory consumption will be too high ::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::calls_number_t calls_number; ///< Block calls number BlockStatistics(::profiler::timestamp_t _duration, ::profiler::block_index_t _block_index) : total_duration(_duration) , min_duration(_duration) , max_duration(_duration) , min_duration_block(_block_index) , max_duration_block(_block_index) , calls_number(1) { } inline ::profiler::timestamp_t average_duration() const { return total_duration / calls_number; } }; // END of struct BlockStatistics. #pragma pack(pop) inline void release(BlockStatistics*& _stats) { if (!_stats) { return; } if (--_stats->calls_number == 0) { delete _stats; } _stats = nullptr; } ////////////////////////////////////////////////////////////////////////// class BlocksTree { typedef BlocksTree This; public: typedef ::std::vector children_t; children_t children; ///< List of children blocks. May be empty. ::profiler::SerializedBlock* node; ///< Pointer to serilized data (type, name, begin, end etc.) ::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::block_index_t block_index; ///< Index of this block unsigned short depth; ///< Maximum number of sublevels (maximum children depth) BlocksTree() : node(nullptr) , per_parent_stats(nullptr) , per_frame_stats(nullptr) , per_thread_stats(nullptr) , block_index(0) , depth(0) { } BlocksTree(This&& that) : BlocksTree() { makeMove(::std::forward(that)); } This& operator = (This&& that) { makeMove(::std::forward(that)); return *this; } ~BlocksTree() { //if (node) //{ // delete node; //} release(per_thread_stats); release(per_parent_stats); release(per_frame_stats); } bool operator < (const This& other) const { if (!node || !other.node) { return false; } return node->block()->getBegin() < other.node->block()->getBegin(); } void shrink_to_fit() { //for (auto& child : children) // child.shrink_to_fit(); // shrink version 1: //children.shrink_to_fit(); // shrink version 2: //children_t new_children; //new_children.reserve(children.size()); //::std::move(children.begin(), children.end(), ::std::back_inserter(new_children)); //new_children.swap(children); } private: BlocksTree(const This&) = delete; This& operator = (const This&) = delete; void makeMove(This&& that) { //if (node && node != that.node) //{ // delete node; //} if (per_thread_stats != that.per_thread_stats) { release(per_thread_stats); } if (per_parent_stats != that.per_parent_stats) { release(per_parent_stats); } if (per_frame_stats != that.per_frame_stats) { release(per_frame_stats); } children = ::std::move(that.children); node = that.node; per_parent_stats = that.per_parent_stats; per_frame_stats = that.per_frame_stats; per_thread_stats = that.per_thread_stats; block_index = that.block_index; depth = that.depth; that.node = nullptr; that.per_parent_stats = nullptr; that.per_frame_stats = nullptr; that.per_thread_stats = nullptr; } }; // END of class BlocksTree. ////////////////////////////////////////////////////////////////////////// class BlocksTreeRoot final { typedef BlocksTreeRoot This; public: BlocksTree tree; const char* thread_name; ::profiler::thread_id_t thread_id; BlocksTreeRoot() : thread_name(""), thread_id(0) { } BlocksTreeRoot(This&& that) : tree(::std::move(that.tree)), thread_name(that.thread_name), thread_id(that.thread_id) { } This& operator = (This&& that) { tree = ::std::move(that.tree); thread_name = that.thread_name; thread_id = that.thread_id; return *this; } bool operator < (const This& other) const { return tree < other.tree; } private: BlocksTreeRoot(const This&) = delete; This& operator = (const This&) = delete; }; // END of class BlocksTreeRoot. typedef ::std::map<::profiler::thread_id_t, ::profiler::BlocksTreeRoot> thread_blocks_tree_t; ////////////////////////////////////////////////////////////////////////// class SerializedData final { char* m_data; public: SerializedData() : m_data(nullptr) { } SerializedData(SerializedData&& that) : m_data(that.m_data) { that.m_data = nullptr; } ~SerializedData() { clear(); } SerializedData& operator = (SerializedData&& that) { clear(); m_data = that.m_data; that.m_data = nullptr; return *this; } char* operator[](uint64_t i) { return m_data + i; } void clear() { if (m_data) { delete[] m_data; m_data = nullptr; } } void set(char* _data) { clear(); m_data = _data; } void swap(SerializedData& other) { auto temp = other.m_data; other.m_data = m_data; m_data = temp; } private: SerializedData(const SerializedData&) = delete; SerializedData& operator = (const SerializedData&) = delete; }; // END of class SerializedData. ////////////////////////////////////////////////////////////////////////// } // END of namespace profiler. extern "C"{ unsigned int PROFILER_API fillTreesFromFile(::std::atomic& progress, const char* filename, ::profiler::SerializedData& serialized_blocks, ::profiler::thread_blocks_tree_t& threaded_trees, bool gather_statistics = false); } inline unsigned int fillTreesFromFile(const char* filename, ::profiler::SerializedData& serialized_blocks, ::profiler::thread_blocks_tree_t& threaded_trees, bool gather_statistics = false) { ::std::atomic progress = ATOMIC_VAR_INIT(0); return fillTreesFromFile(progress, filename, serialized_blocks, threaded_trees, gather_statistics); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #endif // PROFILER_READER____H