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/reader.cpp b/easy_profiler_core/reader.cpp index 0cb6ebc..180b276 100644 --- a/easy_profiler_core/reader.cpp +++ b/easy_profiler_core/reader.cpp @@ -66,17 +66,18 @@ * : limitations under the License. ************************************************************************/ -#include -#include - -#include "hashed_cstr.h" - #include #include #include #include #include +#include +#include + +#include "hashed_cstr.h" +#include "alignment_helpers.h" + ////////////////////////////////////////////////////////////////////////// extern const uint32_t PROFILER_SIGNATURE; @@ -1055,18 +1056,39 @@ struct BlocksRange { profiler::block_index_t begin; profiler::block_index_t end; -}; -struct BlocksAndCSwitchesRange -{ - BlocksRange blocks; - BlocksRange cswitches; + 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 @@ -1085,7 +1107,7 @@ BlocksRange findRange(const profiler::BlocksTree::children_t& children, profiler const profiler::block_getter_fn& getter) { const auto size = static_cast(children.size()); - BlocksRange range {size, size}; + BlocksRange range(size); auto first_it = std::lower_bound(children.begin(), children.end(), beginTime, [&](profiler::block_index_t element, profiler::timestamp_t value) { @@ -1122,6 +1144,92 @@ BlocksRange findRange(const profiler::BlocksTree::children_t& children, profiler return range; } +BlocksMemoryAndCount calculateUsedMemoryAndBlocksCount(const profiler::BlocksTree::children_t& children, + const BlocksRange& range, + const profiler::block_getter_fn& getter, 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 uint64_t 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, + 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 serialize(profiler::SerializedData& output, uint64_t& position, const profiler::BlocksTree::children_t& children, + const BlocksRange& range, const profiler::block_getter_fn& getter, bool contextSwitches) +{ + if (!contextSwitches) + { + // Serialize blocks + + 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())); + serialize(output, position, child.children, childRange, getter, false); + + // Serialize self + const auto usedMemorySize = static_cast( + sizeof(profiler::SerializedBlock) + strlen(child.node->name()) + 1); + + unaligned_store16(output.data() + position, usedMemorySize); + memcpy(output.data() + position + sizeof(uint16_t), child.node, static_cast(usedMemorySize)); + + // TODO: write valid block id (it can be dynamic) + + position += usedMemorySize + sizeof(uint16_t); + } + + return; + } + + // Serialize context switches + 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); + + unaligned_store16(output.data() + position, usedMemorySize); + memcpy(output.data() + position + sizeof(uint16_t), child.cs, static_cast(usedMemorySize)); + + position += usedMemorySize + sizeof(uint16_t); + } +} + extern "C" PROFILER_API profiler::block_index_t writeTreesToFile(std::atomic& progress, const char* filename, const profiler::SerializedData& serialized_descriptors, profiler::block_id_t descriptors_count, @@ -1172,17 +1280,25 @@ extern "C" PROFILER_API profiler::block_index_t writeTreesToStream(std::atomic >; ranges_t block_ranges; - size_t counter = 0, i = 0; + // Calculate block ranges and used memory (for serialization) + size_t i = 0; for (const auto& kv : trees) { const auto id = kv.first; const auto& tree = kv.second; - const auto childrenRange = findRange(tree.children, begin_time, end_time, block_getter); - const auto cswitcesRange = findRange(tree.sync, begin_time, end_time, block_getter); - counter += childrenRange.end - childrenRange.begin; - counter += cswitcesRange.end - cswitcesRange.begin; - block_ranges[id] = BlocksAndCSwitchesRange {childrenRange, cswitcesRange}; + 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, false); + total += range.blocksMemoryAndCount; + + range.cswitchesMemoryAndCount = calculateUsedMemoryAndBlocksCount(tree.sync, range.cswitches, block_getter, true); + total += range.cswitchesMemoryAndCount; + + block_ranges[id] = range; if (!update_progress_write(progress, 15 / static_cast(trees.size() - i), log)) return 0; @@ -1190,29 +1306,73 @@ extern "C" PROFILER_API profiler::block_index_t writeTreesToStream(std::atomic(str, 0LL); // CPU frequency write(str, begin_time); write(str, end_time); write(str, total.usedMemorySize); - write(str, serialized_descriptors.size()); + write(str, usedMemorySizeDescriptors); write(str, total.blocksCount); write(str, descriptors_count); - log << "Not implemented"; - progress.store(100, std::memory_order_release); + // Serialize all descriptors + // TODO + + // Serialize all blocks + profiler::SerializedData serializedBlocks; + serializedBlocks.set(total.usedMemorySize + sizeof(uint16_t) * total.blocksCount); + uint64_t position = 0; + + 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); + write(str, range.cswitchesMemoryAndCount.blocksCount); + + // Serialize context switches + write(str, range.cswitchesMemoryAndCount.blocksCount); + if (range.cswitchesMemoryAndCount.blocksCount != 0) + { + const auto previousPosition = position; + serialize(serializedBlocks, position, tree.sync, range.cswitches, block_getter, true); + write(str, serializedBlocks.data() + previousPosition, position - previousPosition); + } + + // Serialize blocks + write(str, range.blocksMemoryAndCount.blocksCount); + if (range.blocksMemoryAndCount.blocksCount != 0) + { + const auto previousPosition = position; + serialize(serializedBlocks, position, tree.children, range.blocks, block_getter, false); + write(str, serializedBlocks.data() + previousPosition, position - previousPosition); + } + + if (!update_progress_write(progress, 40 + 60 / static_cast(trees.size() - i), log)) + return 0; + } + return total.blocksCount; }