0
0
mirror of https://github.com/yse/easy_profiler.git synced 2024-12-27 08:41:02 +08:00

#91 [UI] .prof serializator (intermediate commit)

This commit is contained in:
Victor Zarubkin 2018-04-23 21:23:47 +03:00
parent 0b9ee4e6d0
commit 21bcefcb75
4 changed files with 459 additions and 245 deletions

View File

@ -62,11 +62,12 @@ profiler::block_index_t FileReader::readFile(const std::string& filename)
profiler::blocks_t blocks; profiler::blocks_t blocks;
profiler::thread_blocks_tree_t threaded_trees; profiler::thread_blocks_tree_t threaded_trees;
profiler::processid_t pid = 0;
uint32_t total_descriptors_number = 0; uint32_t total_descriptors_number = 0;
EASY_CONSTEXPR bool DoNotGatherStats = false; EASY_CONSTEXPR bool DoNotGatherStats = false;
const auto blocks_number = ::fillTreesFromFile(filename.c_str(), serialized_blocks, serialized_descriptors, 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) if (blocks_number == 0)
return 0; return 0;

View File

@ -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 <cstddef>
#include <cstdint>
#include <easy/details/easy_compiler_support.h>
//! 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 <uint32_t ALIGNMENT>
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<alignof(uint64_t)>(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 <typename T>
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 <typename T>
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 <typename T>
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<alignof(T)>(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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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<alignof(T)>(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 <typename T>
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<alignof(T)>(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

View File

@ -45,9 +45,8 @@ The Apache License, Version 2.0 (the "License");
#include <easy/details/easy_compiler_support.h> #include <easy/details/easy_compiler_support.h>
#include <cstring> #include <cstring>
#include <cstddef>
#include <stdint.h>
#include "outstream.h" #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 <uint32_t ALIGNMENT>
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<alignof(uint64_t)>(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 <typename T>
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 <typename T>
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 <typename T>
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<alignof(T)>(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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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<alignof(T)>(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 <typename T>
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<alignof(T)>(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 <const uint16_t N> template <const uint16_t N>
class chunk_allocator class chunk_allocator
{ {

View File

@ -66,17 +66,18 @@
* : limitations under the License. * : limitations under the License.
************************************************************************/ ************************************************************************/
#include <easy/reader.h>
#include <easy/profiler.h>
#include "hashed_cstr.h"
#include <fstream> #include <fstream>
#include <iterator> #include <iterator>
#include <algorithm> #include <algorithm>
#include <unordered_map> #include <unordered_map>
#include <thread> #include <thread>
#include <easy/reader.h>
#include <easy/profiler.h>
#include "hashed_cstr.h"
#include "alignment_helpers.h"
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
extern const uint32_t PROFILER_SIGNATURE; extern const uint32_t PROFILER_SIGNATURE;
@ -1055,18 +1056,39 @@ struct BlocksRange
{ {
profiler::block_index_t begin; profiler::block_index_t begin;
profiler::block_index_t end; profiler::block_index_t end;
};
struct BlocksAndCSwitchesRange BlocksRange(profiler::block_index_t size = 0)
: begin(size), end(size)
{ {
BlocksRange blocks; }
BlocksRange cswitches;
BlocksRange(profiler::block_index_t beginIndex, profiler::block_index_t endIndex)
: begin(beginIndex), end(endIndex)
{
}
}; };
struct BlocksMemoryAndCount struct BlocksMemoryAndCount
{ {
uint64_t usedMemorySize = 0; // memory size used by profiler blocks uint64_t usedMemorySize = 0; // memory size used by profiler blocks
profiler::block_index_t blocksCount = 0; 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 <typename T> template <typename T>
@ -1085,7 +1107,7 @@ BlocksRange findRange(const profiler::BlocksTree::children_t& children, profiler
const profiler::block_getter_fn& getter) const profiler::block_getter_fn& getter)
{ {
const auto size = static_cast<profiler::block_index_t>(children.size()); const auto size = static_cast<profiler::block_index_t>(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) 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; 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<profiler::block_index_t>(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<profiler::block_index_t>(child.children.size()));
serialize(output, position, child.children, childRange, getter, false);
// Serialize self
const auto usedMemorySize = static_cast<uint16_t>(
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<size_t>(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<uint16_t>(
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<size_t>(usedMemorySize));
position += usedMemorySize + sizeof(uint16_t);
}
}
extern "C" PROFILER_API profiler::block_index_t writeTreesToFile(std::atomic<int>& progress, const char* filename, extern "C" PROFILER_API profiler::block_index_t writeTreesToFile(std::atomic<int>& progress, const char* filename,
const profiler::SerializedData& serialized_descriptors, const profiler::SerializedData& serialized_descriptors,
profiler::block_id_t descriptors_count, profiler::block_id_t descriptors_count,
@ -1172,17 +1280,25 @@ extern "C" PROFILER_API profiler::block_index_t writeTreesToStream(std::atomic<i
using ranges_t = std::unordered_map<profiler::thread_id_t, BlocksAndCSwitchesRange, estd::hash<profiler::thread_id_t> >; using ranges_t = std::unordered_map<profiler::thread_id_t, BlocksAndCSwitchesRange, estd::hash<profiler::thread_id_t> >;
ranges_t block_ranges; 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) for (const auto& kv : trees)
{ {
const auto id = kv.first; const auto id = kv.first;
const auto& tree = kv.second; 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; BlocksAndCSwitchesRange range;
counter += cswitcesRange.end - cswitcesRange.begin;
block_ranges[id] = BlocksAndCSwitchesRange {childrenRange, cswitcesRange}; 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<int>(trees.size() - i), log)) if (!update_progress_write(progress, 15 / static_cast<int>(trees.size() - i), log))
return 0; return 0;
@ -1190,29 +1306,73 @@ extern "C" PROFILER_API profiler::block_index_t writeTreesToStream(std::atomic<i
++i; ++i;
} }
if (counter == 0) if (total.blocksCount == 0)
{ {
log << "Nothing to save"; log << "Nothing to save";
return 0; return 0;
} }
const uint64_t usedMemorySizeDescriptors = serialized_descriptors.size() + descriptors_count * sizeof(uint16_t);
// Write data to stream
write(str, PROFILER_SIGNATURE); write(str, PROFILER_SIGNATURE);
write(str, EASY_CURRENT_VERSION); write(str, EASY_CURRENT_VERSION);
write(str, pid); write(str, pid);
// write 0 because we do not need to oncvert time from ticks to nanoseconds (it's already converted here) // write 0 because we do not need to convert time from ticks to nanoseconds (it's already converted)
write<int64_t>(str, 0LL); // CPU frequency write<int64_t>(str, 0LL); // CPU frequency
write(str, begin_time); write(str, begin_time);
write(str, end_time); write(str, end_time);
write(str, total.usedMemorySize); write(str, total.usedMemorySize);
write(str, serialized_descriptors.size()); write(str, usedMemorySizeDescriptors);
write(str, total.blocksCount); write(str, total.blocksCount);
write(str, descriptors_count); write(str, descriptors_count);
log << "Not implemented"; // Serialize all descriptors
progress.store(100, std::memory_order_release); // 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<uint16_t>(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<int>(trees.size() - i), log))
return 0;
}
return total.blocksCount; return total.blocksCount;
} }