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

#91 [Core][UI] functions profiler::writeTreesToFile(), profiler::writeTreesToStream() are now working. Snapshot feature is now working: select an area on Diagram (with right mouse button or double-click) and press Snapshot button to save it to separate file.

This commit is contained in:
Victor Zarubkin 2018-04-25 21:37:18 +03:00
parent 21bcefcb75
commit f665478830
6 changed files with 283 additions and 144 deletions

View File

@ -283,86 +283,41 @@ namespace profiler {
class PROFILER_API SerializedData EASY_FINAL class PROFILER_API SerializedData EASY_FINAL
{ {
char* m_data; uint64_t m_size;
size_t m_size; char* m_data;
public: public:
SerializedData(const SerializedData&) = delete; SerializedData(const SerializedData&) = delete;
SerializedData& operator = (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) SerializedData(SerializedData&& that);
{
that.m_data = nullptr;
that.m_size = 0;
}
~SerializedData() ~SerializedData();
{
clear();
}
void set(uint64_t _size); void set(uint64_t _size);
void extend(uint64_t _size); void extend(uint64_t _size);
SerializedData& operator = (SerializedData&& that) SerializedData& operator = (SerializedData&& that);
{
set(that.m_data, that.m_size);
that.m_data = nullptr;
that.m_size = 0;
return *this;
}
char* operator [] (uint64_t i) char* operator [] (uint64_t i);
{
return m_data + i;
}
const char* operator [] (uint64_t i) const const char* operator [] (uint64_t i) const;
{
return m_data + i;
}
bool empty() const bool empty() const;
{
return m_size == 0;
}
uint64_t size() const uint64_t size() const;
{
return m_size;
}
char* data() char* data();
{
return m_data;
}
const char* data() const const char* data() const;
{
return m_data;
}
void clear() void clear();
{
set(nullptr, 0);
}
void swap(SerializedData& other) 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;
}
private: private:
@ -409,6 +364,7 @@ extern "C" {
PROFILER_API profiler::block_index_t writeTreesToFile(std::atomic<int>& progress, const char* filename, PROFILER_API profiler::block_index_t writeTreesToFile(std::atomic<int>& progress, const char* filename,
const profiler::SerializedData& serialized_descriptors, const profiler::SerializedData& serialized_descriptors,
const profiler::descriptors_list_t& descriptors,
profiler::block_id_t descriptors_count, profiler::block_id_t descriptors_count,
const profiler::thread_blocks_tree_t& trees, const profiler::thread_blocks_tree_t& trees,
profiler::block_getter_fn block_getter, profiler::block_getter_fn block_getter,
@ -419,6 +375,7 @@ extern "C" {
PROFILER_API profiler::block_index_t writeTreesToStream(std::atomic<int>& progress, std::ostream& str, PROFILER_API profiler::block_index_t writeTreesToStream(std::atomic<int>& progress, std::ostream& str,
const profiler::SerializedData& serialized_descriptors, const profiler::SerializedData& serialized_descriptors,
const profiler::descriptors_list_t& descriptors,
profiler::block_id_t descriptors_count, profiler::block_id_t descriptors_count,
const profiler::thread_blocks_tree_t& trees, const profiler::thread_blocks_tree_t& trees,
profiler::block_getter_fn block_getter, profiler::block_getter_fn block_getter,
@ -445,6 +402,7 @@ inline profiler::block_index_t fillTreesFromFile(const char* filename, profiler:
inline profiler::block_index_t writeTreesToFile(const char* filename, inline profiler::block_index_t writeTreesToFile(const char* filename,
const profiler::SerializedData& serialized_descriptors, const profiler::SerializedData& serialized_descriptors,
const profiler::descriptors_list_t& descriptors,
profiler::block_id_t descriptors_count, profiler::block_id_t descriptors_count,
const profiler::thread_blocks_tree_t& trees, const profiler::thread_blocks_tree_t& trees,
profiler::block_getter_fn block_getter, profiler::block_getter_fn block_getter,
@ -454,12 +412,13 @@ inline profiler::block_index_t writeTreesToFile(const char* filename,
std::ostream& log) std::ostream& log)
{ {
std::atomic<int> progress = ATOMIC_VAR_INIT(0); std::atomic<int> progress = ATOMIC_VAR_INIT(0);
return writeTreesToFile(progress, filename, serialized_descriptors, descriptors_count, trees, std::move(block_getter), return writeTreesToFile(progress, filename, serialized_descriptors, descriptors, descriptors_count, trees,
begin_time, end_time, pid, log); std::move(block_getter), begin_time, end_time, pid, log);
} }
inline profiler::block_index_t writeTreesToStream(std::ostream& str, inline profiler::block_index_t writeTreesToStream(std::ostream& str,
const profiler::SerializedData& serialized_descriptors, const profiler::SerializedData& serialized_descriptors,
const profiler::descriptors_list_t& descriptors,
profiler::block_id_t descriptors_count, profiler::block_id_t descriptors_count,
const profiler::thread_blocks_tree_t& trees, const profiler::thread_blocks_tree_t& trees,
profiler::block_getter_fn block_getter, profiler::block_getter_fn block_getter,
@ -469,8 +428,8 @@ inline profiler::block_index_t writeTreesToStream(std::ostream& str,
std::ostream& log) std::ostream& log)
{ {
std::atomic<int> progress = ATOMIC_VAR_INIT(0); std::atomic<int> progress = ATOMIC_VAR_INIT(0);
return writeTreesToStream(progress, str, serialized_descriptors, descriptors_count, trees, std::move(block_getter), return writeTreesToStream(progress, str, serialized_descriptors, descriptors, descriptors_count, trees,
begin_time, end_time, pid, log); std::move(block_getter), begin_time, end_time, pid, log);
} }
inline bool readDescriptionsFromStream(std::istream& str, inline bool readDescriptionsFromStream(std::istream& str,

View File

@ -129,11 +129,26 @@ inline bool isCompatibleVersion(uint32_t _version)
namespace profiler { 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) void SerializedData::set(char* _data, uint64_t _size)
{ {
delete [] m_data; delete [] m_data;
m_data = _data;
m_size = _size; m_size = _size;
m_data = _data;
} }
void SerializedData::set(uint64_t _size) void SerializedData::set(uint64_t _size)
@ -146,8 +161,8 @@ namespace profiler {
void SerializedData::extend(uint64_t _size) void SerializedData::extend(uint64_t _size)
{ {
auto olddata = m_data;
auto oldsize = m_size; auto oldsize = m_size;
auto olddata = m_data;
m_size = oldsize + _size; m_size = oldsize + _size;
m_data = new char[m_size]; m_data = new char[m_size];
@ -158,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) extern "C" PROFILER_API void release_stats(BlockStatistics*& _stats)
{ {
if (_stats == nullptr) if (_stats == nullptr)
@ -653,6 +723,12 @@ extern "C" PROFILER_API profiler::block_index_t fillTreesFromStream(std::atomic<
return 0; return 0;
} }
if (i + sz > memory_size)
{
_log << "File corrupted.\nActual context switches data size > size pointed in file.";
return 0;
}
char* data = serialized_blocks[i]; char* data = serialized_blocks[i];
inFile.read(data, sz); inFile.read(data, sz);
i += sz; i += sz;
@ -714,6 +790,12 @@ extern "C" PROFILER_API profiler::block_index_t fillTreesFromStream(std::atomic<
return 0; return 0;
} }
if (i + sz > memory_size)
{
_log << "File corrupted.\nActual blocks data size > size pointed in file.";
return 0;
}
char* data = serialized_blocks[i]; char* data = serialized_blocks[i];
inFile.read(data, sz); inFile.read(data, sz);
i += sz; i += sz;
@ -1146,7 +1228,9 @@ BlocksRange findRange(const profiler::BlocksTree::children_t& children, profiler
BlocksMemoryAndCount calculateUsedMemoryAndBlocksCount(const profiler::BlocksTree::children_t& children, BlocksMemoryAndCount calculateUsedMemoryAndBlocksCount(const profiler::BlocksTree::children_t& children,
const BlocksRange& range, const BlocksRange& range,
const profiler::block_getter_fn& getter, bool contextSwitches) const profiler::block_getter_fn& getter,
const profiler::descriptors_list_t& descriptors,
bool contextSwitches)
{ {
BlocksMemoryAndCount memoryAndCount; BlocksMemoryAndCount memoryAndCount;
@ -1157,12 +1241,18 @@ BlocksMemoryAndCount calculateUsedMemoryAndBlocksCount(const profiler::BlocksTre
const auto& child = getter(children[i]); const auto& child = getter(children[i]);
// Calculate self memory consumption // Calculate self memory consumption
const uint64_t usedMemorySize = sizeof(profiler::SerializedBlock) + strlen(child.node->name()) + 1; 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 // Calculate children memory consumption
const BlocksRange childRange(0, static_cast<profiler::block_index_t>(child.children.size())); const BlocksRange childRange(0, static_cast<profiler::block_index_t>(child.children.size()));
const auto childrenMemoryAndCount = calculateUsedMemoryAndBlocksCount(child.children, childRange, const auto childrenMemoryAndCount = calculateUsedMemoryAndBlocksCount(child.children, childRange,
getter, getter, descriptors,
false); false);
// Accumulate memory and count // Accumulate memory and count
@ -1185,37 +1275,53 @@ BlocksMemoryAndCount calculateUsedMemoryAndBlocksCount(const profiler::BlocksTre
return memoryAndCount; return memoryAndCount;
} }
void serialize(profiler::SerializedData& output, uint64_t& position, const profiler::BlocksTree::children_t& children, void serializeBlocks(std::ostream& output, std::vector<char>& buffer, const profiler::BlocksTree::children_t& children,
const BlocksRange& range, const profiler::block_getter_fn& getter, bool contextSwitches) const BlocksRange& range, const profiler::block_getter_fn& getter,
const profiler::descriptors_list_t& descriptors)
{ {
if (!contextSwitches) for (auto i = range.begin; i < range.end; ++i)
{ {
// Serialize blocks const auto& child = getter(children[i]);
for (auto i = range.begin; i < range.end; ++i) // Serialize children
const BlocksRange childRange(0, static_cast<profiler::block_index_t>(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)
{ {
const auto& child = getter(children[i]); usedMemorySize = static_cast<uint16_t>(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<size_t>(usedMemorySize));
}
else
{
usedMemorySize = static_cast<uint16_t>(sizeof(profiler::SerializedBlock)
+ strlen(child.node->name()) + 1);
// Serialize children buffer.resize(usedMemorySize + sizeof(uint16_t));
const BlocksRange childRange(0, static_cast<profiler::block_index_t>(child.children.size())); unaligned_store16(buffer.data(), usedMemorySize);
serialize(output, position, child.children, childRange, getter, false); memcpy(buffer.data() + sizeof(uint16_t), child.node, static_cast<size_t>(usedMemorySize));
// Serialize self if (child.node->id() != desc.id())
const auto usedMemorySize = static_cast<uint16_t>( {
sizeof(profiler::SerializedBlock) + strlen(child.node->name()) + 1); // This block id is dynamic. Restore it's value like it was before in the input .prof file
auto block = reinterpret_cast<profiler::SerializedBlock*>(buffer.data() + sizeof(uint16_t));
unaligned_store16(output.data() + position, usedMemorySize); block->setId(desc.id());
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; write(output, buffer.data(), buffer.size());
} }
}
// Serialize context switches void serializeContextSwitches(std::ostream& output, std::vector<char>& 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) for (auto i = range.begin; i < range.end; ++i)
{ {
const auto& child = getter(children[i]); const auto& child = getter(children[i]);
@ -1223,15 +1329,39 @@ void serialize(profiler::SerializedData& output, uint64_t& position, const profi
const auto usedMemorySize = static_cast<uint16_t>( const auto usedMemorySize = static_cast<uint16_t>(
sizeof(profiler::SerializedCSwitch) + strlen(child.cs->name()) + 1); sizeof(profiler::SerializedCSwitch) + strlen(child.cs->name()) + 1);
unaligned_store16(output.data() + position, usedMemorySize); buffer.resize(usedMemorySize + sizeof(uint16_t));
memcpy(output.data() + position + sizeof(uint16_t), child.cs, static_cast<size_t>(usedMemorySize)); unaligned_store16(buffer.data(), usedMemorySize);
memcpy(buffer.data() + sizeof(uint16_t), child.cs, static_cast<size_t>(usedMemorySize));
position += usedMemorySize + sizeof(uint16_t); write(output, buffer.data(), buffer.size());
}
}
void serializeDescriptors(std::ostream& output, std::vector<char>& buffer,
const profiler::descriptors_list_t& descriptors,
profiler::block_id_t descriptors_count)
{
const size_t size = std::min(descriptors.size(), static_cast<size_t>(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<uint16_t>(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<size_t>(usedMemorySize));
write(output, buffer.data(), buffer.size());
} }
} }
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,
const profiler::descriptors_list_t& descriptors,
profiler::block_id_t descriptors_count, profiler::block_id_t descriptors_count,
const profiler::thread_blocks_tree_t& trees, const profiler::thread_blocks_tree_t& trees,
profiler::block_getter_fn block_getter, profiler::block_getter_fn block_getter,
@ -1251,8 +1381,8 @@ extern "C" PROFILER_API profiler::block_index_t writeTreesToFile(std::atomic<int
} }
// Write data to file // Write data to file
auto result = writeTreesToStream(progress, outFile, serialized_descriptors, descriptors_count, trees, std::move(block_getter), auto result = writeTreesToStream(progress, outFile, serialized_descriptors, descriptors, descriptors_count, trees,
begin_time, end_time, pid, log); std::move(block_getter), begin_time, end_time, pid, log);
return result; return result;
} }
@ -1261,6 +1391,7 @@ extern "C" PROFILER_API profiler::block_index_t writeTreesToFile(std::atomic<int
extern "C" PROFILER_API profiler::block_index_t writeTreesToStream(std::atomic<int>& progress, std::ostream& str, extern "C" PROFILER_API profiler::block_index_t writeTreesToStream(std::atomic<int>& progress, std::ostream& str,
const profiler::SerializedData& serialized_descriptors, const profiler::SerializedData& serialized_descriptors,
const profiler::descriptors_list_t& descriptors,
profiler::block_id_t descriptors_count, profiler::block_id_t descriptors_count,
const profiler::thread_blocks_tree_t& trees, const profiler::thread_blocks_tree_t& trees,
profiler::block_getter_fn block_getter, profiler::block_getter_fn block_getter,
@ -1281,6 +1412,7 @@ extern "C" PROFILER_API profiler::block_index_t writeTreesToStream(std::atomic<i
ranges_t block_ranges; ranges_t block_ranges;
// Calculate block ranges and used memory (for serialization) // Calculate block ranges and used memory (for serialization)
profiler::timestamp_t beginTime = begin_time, endTime = end_time;
size_t i = 0; size_t i = 0;
for (const auto& kv : trees) for (const auto& kv : trees)
{ {
@ -1292,12 +1424,26 @@ extern "C" PROFILER_API profiler::block_index_t writeTreesToStream(std::atomic<i
range.blocks = findRange(tree.children, begin_time, end_time, block_getter); range.blocks = findRange(tree.children, begin_time, end_time, block_getter);
range.cswitches = findRange(tree.sync, 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); range.blocksMemoryAndCount = calculateUsedMemoryAndBlocksCount(tree.children, range.blocks, block_getter,
descriptors, false);
total += range.blocksMemoryAndCount; total += range.blocksMemoryAndCount;
range.cswitchesMemoryAndCount = calculateUsedMemoryAndBlocksCount(tree.sync, range.cswitches, block_getter, true); 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; 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; 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))
@ -1322,22 +1468,20 @@ extern "C" PROFILER_API profiler::block_index_t writeTreesToStream(std::atomic<i
// write 0 because we do not need to convert time from ticks to nanoseconds (it's already converted) // 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, beginTime);
write(str, end_time); write(str, endTime);
write(str, total.usedMemorySize); write(str, total.usedMemorySize);
write(str, usedMemorySizeDescriptors); write(str, usedMemorySizeDescriptors);
write(str, total.blocksCount); write(str, total.blocksCount);
write(str, descriptors_count); write(str, descriptors_count);
std::vector<char> buffer;
// Serialize all descriptors // Serialize all descriptors
// TODO serializeDescriptors(str, buffer, descriptors, descriptors_count);
// Serialize all blocks // Serialize all blocks
profiler::SerializedData serializedBlocks;
serializedBlocks.set(total.usedMemorySize + sizeof(uint16_t) * total.blocksCount);
uint64_t position = 0;
i = 0; i = 0;
for (const auto& kv : trees) for (const auto& kv : trees)
{ {
@ -1349,25 +1493,16 @@ extern "C" PROFILER_API profiler::block_index_t writeTreesToStream(std::atomic<i
write(str, id); write(str, id);
write(str, nameSize); write(str, nameSize);
write(str, tree.name(), nameSize); write(str, tree.name(), nameSize);
write(str, range.cswitchesMemoryAndCount.blocksCount);
// Serialize context switches // Serialize context switches
write(str, range.cswitchesMemoryAndCount.blocksCount); write(str, range.cswitchesMemoryAndCount.blocksCount);
if (range.cswitchesMemoryAndCount.blocksCount != 0) if (range.cswitchesMemoryAndCount.blocksCount != 0)
{ serializeContextSwitches(str, buffer, tree.sync, range.cswitches, block_getter);
const auto previousPosition = position;
serialize(serializedBlocks, position, tree.sync, range.cswitches, block_getter, true);
write(str, serializedBlocks.data() + previousPosition, position - previousPosition);
}
// Serialize blocks // Serialize blocks
write(str, range.blocksMemoryAndCount.blocksCount); write(str, range.blocksMemoryAndCount.blocksCount);
if (range.blocksMemoryAndCount.blocksCount != 0) if (range.blocksMemoryAndCount.blocksCount != 0)
{ serializeBlocks(str, buffer, tree.children, range.blocks, block_getter, descriptors);
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)) if (!update_progress_write(progress, 40 + 60 / static_cast<int>(trees.size() - i), log))
return 0; return 0;

View File

@ -407,6 +407,8 @@ void BlocksGraphicsView::clear()
EASY_GLOBALS.selected_thread = 0; EASY_GLOBALS.selected_thread = 0;
emit EASY_GLOBALS.events.selectedThreadChanged(0); emit EASY_GLOBALS.events.selectedThreadChanged(0);
emit EASY_GLOBALS.events.rulerVisible(false);
} }
void BlocksGraphicsView::notifySceneSizeChange() void BlocksGraphicsView::notifySceneSizeChange()
@ -438,7 +440,10 @@ void BlocksGraphicsView::notifyVisibleRegionPosChange()
void BlocksGraphicsView::notifyVisibleRegionPosChange(qreal _pos) 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(); notifyVisibleRegionPosChange();
} }
@ -616,25 +621,15 @@ const BlocksGraphicsView::Items &BlocksGraphicsView::getItems() const
bool BlocksGraphicsView::getSelectionRegionForSaving(profiler::timestamp_t& _beginTime, profiler::timestamp_t& _endTime) const bool BlocksGraphicsView::getSelectionRegionForSaving(profiler::timestamp_t& _beginTime, profiler::timestamp_t& _endTime) const
{ {
if (m_selectedBlocks.empty()) if (m_bEmpty)
return false; return false;
_beginTime = ~0ULL; if (!m_selectionItem->isVisible() && !m_rulerItem->isVisible())
_endTime = 0; return false;
for (const auto& selection : m_selectedBlocks) decltype(m_selectionItem) ruler = m_selectionItem->isVisible() ? m_selectionItem : m_rulerItem;
{ _beginTime = m_beginTime + position2time(ruler->left());
const auto& tree = easyBlocksTree(selection.tree); _endTime = m_beginTime + position2time(ruler->right());
_beginTime = std::min(_beginTime, tree.node->begin());
_endTime = std::max(_endTime, tree.node->end());
}
if (_beginTime > 10)
_beginTime -= 10;
else
_beginTime = 0;
_endTime += 10;
return true; return true;
} }
@ -923,7 +918,10 @@ void BlocksGraphicsView::onWheel(qreal _scenePos, int _wheelDelta)
notifyVisibleRegionSizeChange(); notifyVisibleRegionSizeChange();
// Calculate new offset to simulate QGraphicsView::AnchorUnderMouse scaling behavior // 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 // Update slider position
profiler_gui::BoolFlagGuard guard(m_bUpdatingRect, true); // To be sure that updateVisibleSceneRect will not be called by scrollbar change profiler_gui::BoolFlagGuard guard(m_bUpdatingRect, true); // To be sure that updateVisibleSceneRect will not be called by scrollbar change
@ -980,6 +978,7 @@ void BlocksGraphicsView::mousePressEvent(QMouseEvent* _event)
m_selectionItem->setLeftRight(mouseX, mouseX); m_selectionItem->setLeftRight(mouseX, mouseX);
m_selectionItem->hide(); m_selectionItem->hide();
m_pScrollbar->hideSelectionIndicator(); m_pScrollbar->hideSelectionIndicator();
emit EASY_GLOBALS.events.rulerVisible(m_rulerItem->isVisible());
} }
} }
@ -1009,6 +1008,7 @@ void BlocksGraphicsView::mouseDoubleClickEvent(QMouseEvent* _event)
m_rulerItem->setLeftRight(mouseX, mouseX); m_rulerItem->setLeftRight(mouseX, mouseX);
m_rulerItem->hide(); m_rulerItem->hide();
emit sceneUpdated(); emit sceneUpdated();
emit EASY_GLOBALS.events.rulerVisible(m_selectionItem->isVisible());
} }
_event->accept(); _event->accept();
@ -1035,6 +1035,7 @@ void BlocksGraphicsView::mouseReleaseEvent(QMouseEvent* _event)
{ {
m_selectionItem->hide(); m_selectionItem->hide();
m_pScrollbar->hideSelectionIndicator(); m_pScrollbar->hideSelectionIndicator();
emit EASY_GLOBALS.events.rulerVisible(m_rulerItem->isVisible());
} }
if (!m_selectedBlocks.empty()) if (!m_selectedBlocks.empty())
@ -1071,6 +1072,7 @@ void BlocksGraphicsView::mouseReleaseEvent(QMouseEvent* _event)
{ {
chronoHidden = true; chronoHidden = true;
m_rulerItem->hide(); m_rulerItem->hide();
emit EASY_GLOBALS.events.rulerVisible(m_selectionItem->isVisible());
} }
else if (m_selectionItem->isVisible() && m_selectionItem->hoverIndicator()) else if (m_selectionItem->isVisible() && m_selectionItem->hoverIndicator())
{ {
@ -1216,6 +1218,9 @@ void BlocksGraphicsView::addSelectionToHierarchy()
void BlocksGraphicsView::onZoomSelection() void BlocksGraphicsView::onZoomSelection()
{ {
if (m_selectionItem->width() < 1e-6)
return;
auto deltaScale = m_visibleRegionWidth / m_selectionItem->width(); auto deltaScale = m_visibleRegionWidth / m_selectionItem->width();
if (fabs(deltaScale - 1) < 1e-6) if (fabs(deltaScale - 1) < 1e-6)
@ -1254,6 +1259,9 @@ void BlocksGraphicsView::onInspectCurrentView(bool _strict)
m_selectionItem->show(); m_selectionItem->show();
m_pScrollbar->setSelectionPos(m_selectionItem->left(), m_selectionItem->right()); m_pScrollbar->setSelectionPos(m_selectionItem->left(), m_selectionItem->right());
m_pScrollbar->showSelectionIndicator(); m_pScrollbar->showSelectionIndicator();
emit EASY_GLOBALS.events.rulerVisible(true);
addSelectionToHierarchy(); addSelectionToHierarchy();
} }
else else
@ -1306,6 +1314,7 @@ bool BlocksGraphicsView::moveChrono(GraphicsRulerItem* _chronometerItem, qreal _
if (!_chronometerItem->isVisible() && _chronometerItem->width() > 1e-6) if (!_chronometerItem->isVisible() && _chronometerItem->width() > 1e-6)
{ {
_chronometerItem->show(); _chronometerItem->show();
emit EASY_GLOBALS.events.rulerVisible(true);
return true; return true;
} }

View File

@ -93,6 +93,7 @@ namespace profiler_gui {
void hexThreadIdChanged(); void hexThreadIdChanged();
void refreshRequired(); void refreshRequired();
void blocksTreeModeChanged(); void blocksTreeModeChanged();
void rulerVisible(bool);
void sceneCleared(); void sceneCleared();
void sceneSizeChanged(qreal left, qreal right); void sceneSizeChanged(qreal left, qreal right);

View File

@ -379,8 +379,10 @@ MainWindow::MainWindow() : Parent(), m_theme("default"), m_lastAddress("localhos
}); });
action = toolbar->addAction(QIcon(imagePath("crop")), "Snapshot"); action = toolbar->addAction(QIcon(imagePath("crop")), "Snapshot");
action->setToolTip("Save selected area\nas separate .prof file."); action->setToolTip("Take a snapshot.\nSave selected area to\nseparate .prof file.");
action->setEnabled(false);
connect(action, &QAction::triggered, this, &This::onSnapshotClicked); connect(action, &QAction::triggered, this, &This::onSnapshotClicked);
connect(&EASY_GLOBALS.events, &profiler_gui::GlobalSignals::rulerVisible, action, &QAction::setEnabled);
toolbar->addSeparator(); toolbar->addSeparator();
auto menu = new QMenu("Settings", this); auto menu = new QMenu("Settings", this);
@ -1944,7 +1946,8 @@ void MainWindow::onLoadingFinish(profiler::block_index_t& _nblocks)
} }
else else
{ {
QMessageBox::warning(this, "Warning", QString("Cannot read profiled blocks.\n\nReason:\n%1").arg(m_reader.getError()), QMessageBox::Close); QMessageBox::warning(this, "Warning", QString("Cannot read profiled blocks.\n\nReason:\n%1")
.arg(m_reader.getError()), QMessageBox::Close);
if (m_reader.isFile()) if (m_reader.isFile())
{ {
@ -1964,7 +1967,12 @@ void MainWindow::onLoadingFinish(profiler::block_index_t& _nblocks)
void MainWindow::onSavingFinish() 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() void MainWindow::onFileReaderTimeout()
@ -2114,7 +2122,8 @@ void FileReader::load(std::stringstream& _stream)
} }
void FileReader::save(const QString& _filename, profiler::timestamp_t _beginTime, profiler::timestamp_t _endTime, void FileReader::save(const QString& _filename, profiler::timestamp_t _beginTime, profiler::timestamp_t _endTime,
const profiler::SerializedData& _serializedDescriptors, profiler::block_id_t descriptors_count, const profiler::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, const profiler::thread_blocks_tree_t& _trees, profiler::block_getter_fn block_getter,
profiler::processid_t _pid) profiler::processid_t _pid)
{ {
@ -2125,13 +2134,37 @@ void FileReader::save(const QString& _filename, profiler::timestamp_t _beginTime
m_filename = _filename; m_filename = _filename;
auto serializedDescriptors = std::ref(_serializedDescriptors); auto serializedDescriptors = std::ref(_serializedDescriptors);
auto descriptors = std::ref(_descriptors);
auto trees = std::ref(_trees); auto trees = std::ref(_trees);
m_thread = std::thread([=] (profiler::block_getter_fn getter) { m_thread = std::thread([=] (profiler::block_getter_fn getter)
writeTreesToFile(m_progress, m_filename.toStdString().c_str(), serializedDescriptors, descriptors_count, trees, {
getter, _beginTime, _endTime, _pid, m_errorMessage); 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_progress.store(100, std::memory_order_release);
m_bDone.store(true, std::memory_order_release); m_bDone.store(true, std::memory_order_release);
}, std::move(block_getter)); }, std::move(block_getter));
} }
@ -2254,8 +2287,9 @@ void MainWindow::onSnapshotClicked(bool)
createProgressDialog(tr("Saving selected region...")); createProgressDialog(tr("Saving selected region..."));
m_readerTimer.start(); m_readerTimer.start();
m_reader.save(filename, beginTime, endTime, m_serializedDescriptors, m_descriptorsNumberInFile,
EASY_GLOBALS.profiler_blocks, easyBlocksTree, EASY_GLOBALS.pid); m_reader.save(filename, beginTime, endTime, m_serializedDescriptors, EASY_GLOBALS.descriptors,
m_descriptorsNumberInFile, EASY_GLOBALS.profiler_blocks, easyBlocksTree, EASY_GLOBALS.pid);
} }
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////

View File

@ -130,8 +130,9 @@ public:
/** \brief Save data to file. /** \brief Save data to file.
*/ */
void save(const QString& _filename, profiler::timestamp_t _beginTime, profiler::timestamp_t _endTime, void save(const QString& _filename, profiler::timestamp_t _beginTime, profiler::timestamp_t _endTime,
const profiler::SerializedData& _serializedDescriptors, profiler::block_id_t descriptors_count, const profiler::SerializedData& _serializedDescriptors, const profiler::descriptors_list_t& _descriptors,
const profiler::thread_blocks_tree_t& _trees, profiler::block_getter_fn block_getter, profiler::processid_t _pid); 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 interrupt();
void get(profiler::SerializedData& _serializedBlocks, profiler::SerializedData& _serializedDescriptors, void get(profiler::SerializedData& _serializedBlocks, profiler::SerializedData& _serializedDescriptors,