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

update #42 : Slightly better solution with less amount of black magic - different data structures for context switch events. Core API changed!

This commit is contained in:
Victor Zarubkin 2017-06-07 01:39:45 +03:00
parent b14dbef78d
commit 93c3066095
9 changed files with 132 additions and 87 deletions

View File

@ -54,17 +54,25 @@
using namespace profiler;
#ifndef EASY_PROFILER_API_DISABLED
Event::Event(timestamp_t _begin_time) : m_begin(_begin_time), m_end(0)
{
}
Event::Event(timestamp_t _begin_time, timestamp_t _end_time) : m_begin(_begin_time), m_end(_end_time)
{
}
BaseBlockData::BaseBlockData(timestamp_t _begin_time, block_id_t _descriptor_id)
: m_begin(_begin_time)
, m_end(0)
: Event(_begin_time)
, m_id(_descriptor_id)
{
}
BaseBlockData::BaseBlockData(timestamp_t _begin_time, timestamp_t _end_time, block_id_t _descriptor_id)
: m_begin(_begin_time)
, m_end(_end_time)
: Event(_begin_time, _end_time)
, m_id(_descriptor_id)
{
@ -133,9 +141,25 @@ Block::~Block()
::profiler::endBlock();
}
#else
Event::Event(timestamp_t) : m_begin(0), m_end(0)
{
}
Event::Event(timestamp_t, timestamp_t) : m_begin(0), m_end(0)
{
}
BaseBlockData::BaseBlockData(timestamp_t, block_id_t)
: m_begin(0)
, m_end(0)
: Event(0, 0)
, m_id(~0U)
{
}
BaseBlockData::BaseBlockData(timestamp_t, timestamp_t, block_id_t)
: Event(0, 0)
, m_id(~0U)
{
@ -199,16 +223,16 @@ Block::~Block()
//////////////////////////////////////////////////////////////////////////
CSwitchBlock::CSwitchBlock(timestamp_t _begin_time, thread_id_t _tid, const char* _runtimeName)
: Block(_begin_time, _begin_time, 0, _runtimeName)
CSwitchEvent::CSwitchEvent(timestamp_t _begin_time, thread_id_t _tid)
: Event(_begin_time)
, m_thread_id(_tid)
{
}
CSwitchBlock::CSwitchBlock(CSwitchBlock&& _that)
: Block(std::forward<Block>(_that))
, m_thread_id(_that.m_thread_id)
CSwitchBlock::CSwitchBlock(timestamp_t _begin_time, thread_id_t _tid, const char* _runtimeName)
: CSwitchEvent(_begin_time, _tid)
, m_name(_runtimeName)
{
}

View File

@ -493,7 +493,7 @@ namespace profiler {
//***********************************************
class PROFILER_API BaseBlockData
class PROFILER_API Event
{
friend ::ProfileManager;
@ -501,6 +501,29 @@ namespace profiler {
timestamp_t m_begin;
timestamp_t m_end;
public:
Event(const Event&) = default;
Event(timestamp_t _begin_time);
Event(timestamp_t _begin_time, timestamp_t _end_time);
inline timestamp_t begin() const { return m_begin; }
inline timestamp_t end() const { return m_end; }
inline timestamp_t duration() const { return m_end - m_begin; }
private:
Event() = delete;
}; // END class Event.
class PROFILER_API BaseBlockData : public Event
{
friend ::ProfileManager;
protected:
block_id_t m_id;
public:
@ -509,11 +532,7 @@ namespace profiler {
BaseBlockData(timestamp_t _begin_time, block_id_t _id);
BaseBlockData(timestamp_t _begin_time, timestamp_t _end_time, block_id_t _id);
inline timestamp_t begin() const { return m_begin; }
inline timestamp_t end() const { return m_end; }
inline block_id_t id() const { return m_id; }
inline timestamp_t duration() const { return m_end - m_begin; }
inline void setId(block_id_t _id) { m_id = _id; }
private:
@ -521,6 +540,20 @@ namespace profiler {
BaseBlockData() = delete;
}; // END of class BaseBlockData.
class PROFILER_API CSwitchEvent : public Event
{
thread_id_t m_thread_id;
public:
CSwitchEvent() = default;
CSwitchEvent(const CSwitchEvent&) = default;
CSwitchEvent(timestamp_t _begin_time, thread_id_t _tid);
inline thread_id_t tid() const { return m_thread_id; }
}; // END of class CSwitchEvent.
#pragma pack(pop)
//***********************************************

View File

@ -121,7 +121,12 @@ namespace profiler {
typedef ::std::vector<::profiler::block_index_t> children_t;
children_t children; ///< List of children blocks. May be empty.
::profiler::SerializedBlock* node; ///< Pointer to serilized data (type, name, begin, end etc.)
union {
::profiler::SerializedBlock* node; ///< Pointer to serilized data for regular block (id, name, begin, end etc.)
::profiler::SerializedCSwitch* cs; ///< Pointer to serilized data for context switch (thread_id, 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

View File

@ -51,7 +51,7 @@ namespace profiler {
//////////////////////////////////////////////////////////////////////////
class PROFILER_API SerializedBlock : public BaseBlockData
class PROFILER_API SerializedBlock EASY_FINAL : public BaseBlockData
{
friend ::ProfileManager;
friend ::ThreadStorage;
@ -63,68 +63,43 @@ namespace profiler {
///< Run-time block name is stored right after main BaseBlockData data
inline const char* name() const { return data() + sizeof(BaseBlockData); }
protected:
private:
SerializedBlock(const ::profiler::Block& block, uint16_t name_length);
SerializedBlock(const Block& block, uint16_t name_length);
SerializedBlock(const SerializedBlock&) = delete;
SerializedBlock& operator = (const SerializedBlock&) = delete;
//TODO yse: reason of deleted
//~SerializedBlock() = delete;
protected:
SerializedBlock(const ::profiler::Block& block);
~SerializedBlock() = delete;
}; // END of SerializedBlock.
//////////////////////////////////////////////////////////////////////////
#pragma pack(push, 1)
/** This is serious work-around to be able to read/write valid
thread ids after changing thread_id_t from 32-bit to 64-bit.
Before v1.3.0 thread_id_t was uint32_t and has been stored in BaseBlockData::m_id.
After v1.3.0 we have to allocate additional 4 bytes per context switch block to
be able to store 64-bit value.
This is bad design decision at first look, but it has backward compatibility,
does not require serious changes to Core API (except this particular class)
and saves 4 bytes per context switch block.
TODO: think about better solution.
*/
class PROFILER_API SerializedCSwitch : public SerializedBlock
class PROFILER_API SerializedCSwitch EASY_FINAL : public CSwitchEvent
{
friend ::ProfileManager;
friend ::ThreadStorage;
uint32_t m_reserve; /** Additional 4 bytes used to store second part of thread_id_t
(first part is stored in BaseBlockData::m_id as it was before
changing thread_id_t to 64-bit) */
public:
///< Thread id is stored in m_id + m_reserve
inline thread_id_t tid() const { return *reinterpret_cast<const thread_id_t*>(&m_id); }
inline const char* data() const { return reinterpret_cast<const char*>(this); }
///< Run-time block name is stored right after main BaseBlockData data and reserved 4 bytes block
inline const char* name() const { return data() + sizeof(BaseBlockData) + 4; }
///< Run-time block name is stored right after main CSwitchEvent data
inline const char* name() const { return data() + sizeof(CSwitchEvent); }
private:
SerializedCSwitch(const CSwitchBlock& block, uint16_t name_length);
SerializedCSwitch(const SerializedBlock&) = delete;
SerializedCSwitch& operator = (const SerializedBlock&) = delete;
SerializedCSwitch(const SerializedCSwitch&) = delete;
SerializedCSwitch& operator = (const SerializedCSwitch&) = delete;
~SerializedCSwitch() = delete;
}; // END of SerializedCSwitch.
//////////////////////////////////////////////////////////////////////////
#pragma pack(push, 1)
class PROFILER_API SerializedBlockDescriptor EASY_FINAL : public BaseBlockDescriptor
{
uint16_t m_nameLength; ///< Length of the name including trailing '\0' sybmol

View File

@ -519,17 +519,9 @@ SerializedBlock::SerializedBlock(const Block& block, uint16_t name_length)
pName[name_length] = 0;
}
SerializedBlock::SerializedBlock(const Block& block)
: BaseBlockData(block)
{
}
SerializedCSwitch::SerializedCSwitch(const CSwitchBlock& block, uint16_t name_length)
: SerializedBlock(block)
: CSwitchEvent(block)
{
*reinterpret_cast<thread_id_t*>(&m_id) = block.tid();
auto pName = const_cast<char*>(name());
if (name_length) strncpy(pName, block.name(), name_length);
pName[name_length] = 0;
@ -681,7 +673,7 @@ void ThreadStorage::storeBlock(const profiler::Block& block)
void ThreadStorage::storeCSwitch(const CSwitchBlock& block)
{
auto name_length = static_cast<uint16_t>(strlen(block.name()));
auto size = static_cast<uint16_t>(sizeof(BaseBlockData) + name_length + 5);
auto size = static_cast<uint16_t>(sizeof(CSwitchEvent) + name_length + 1);
auto data = sync.closedList.allocate(size);
::new (data) SerializedCSwitch(block, name_length);
sync.usedMemorySize += size;

View File

@ -421,29 +421,20 @@ struct BlocksList
//////////////////////////////////////////////////////////////////////////
class CSwitchBlock : public ::profiler::Block
class CSwitchBlock : public profiler::CSwitchEvent
{
::profiler::thread_id_t m_thread_id;
const char* m_name;
public:
CSwitchBlock(::profiler::timestamp_t _begin_time, ::profiler::thread_id_t _tid, const char* _runtimeName);
CSwitchBlock(CSwitchBlock&& _that);
inline ::profiler::thread_id_t tid() const {
return m_thread_id;
}
private:
CSwitchBlock(const CSwitchBlock&) = delete;
CSwitchBlock& operator = (const CSwitchBlock&) = delete;
CSwitchBlock(profiler::timestamp_t _begin_time, profiler::thread_id_t _tid, const char* _runtimeName);
inline const char* name() const { return m_name; }
};
//////////////////////////////////////////////////////////////////////////
const uint16_t SIZEOF_BLOCK = sizeof(profiler::BaseBlockData) + 1 + sizeof(uint16_t); // SerializedBlock stores BaseBlockData + at least 1 character for name ('\0') + 2 bytes for size of serialized data
const uint16_t SIZEOF_CSWITCH = SIZEOF_BLOCK + 4; // SerializedCSwitch also stores additional 4 bytes to be able to save 64-bit thread_id
const uint16_t SIZEOF_CSWITCH = sizeof(profiler::CSwitchEvent) + 1 + sizeof(uint16_t); // SerializedCSwitch also stores additional 4 bytes to be able to save 64-bit thread_id
struct ThreadStorage
{

View File

@ -609,7 +609,7 @@ extern "C" {
char* data = serialized_blocks[i];
inFile.read(data, sz);
i += sz;
auto baseData = reinterpret_cast<::profiler::SerializedBlock*>(data);
auto baseData = reinterpret_cast<::profiler::SerializedCSwitch*>(data);
auto t_begin = reinterpret_cast<::profiler::timestamp_t*>(data);
auto t_end = t_begin + 1;
@ -626,7 +626,7 @@ extern "C" {
blocks.emplace_back();
::profiler::BlocksTree& tree = blocks.back();
tree.node = baseData;
tree.cs = baseData;
const auto block_index = blocks_counter++;
root.wait_time += baseData->duration();

View File

@ -1711,7 +1711,19 @@ void EasyGraphicsView::onIdleTimeout()
lay->addWidget(new QLabel("Thread:", widget), row, 0, Qt::AlignRight);
const ::profiler::thread_id_t tid = EASY_GLOBALS.version < ::profiler_gui::V130 ? cse->tree.node->id() : static_cast<const ::profiler::SerializedCSwitch*>(cse->tree.node)->tid();
const char* process_name = "";
::profiler::thread_id_t tid = 0;
if (EASY_GLOBALS.version < ::profiler_gui::V130)
{
tid = cse->tree.node->id();
process_name = cse->tree.node->name();
}
else
{
tid = cse->tree.cs->tid();
process_name = cse->tree.cs->name();
}
auto it = EASY_GLOBALS.profiler_blocks.find(tid);
if (it != EASY_GLOBALS.profiler_blocks.end())
@ -1728,7 +1740,7 @@ void EasyGraphicsView::onIdleTimeout()
++row;
lay->addWidget(new QLabel("Process:", widget), row, 0, Qt::AlignRight);
lay->addWidget(new QLabel(cse->tree.node->name(), widget), row, 1, 1, 2, Qt::AlignLeft);
lay->addWidget(new QLabel(process_name, widget), row, 1, 1, 2, Qt::AlignLeft);
++row;
const auto duration = itemBlock.node->duration();
@ -2139,10 +2151,7 @@ void EasyThreadNamesWidget::removePopup(bool _removeFromScene)
delete widget;
if (_removeFromScene)
{
scene()->removeItem(m_popupWidget);
setFixedWidth(m_maxLength);
}
m_popupWidget = nullptr;
}
@ -2221,22 +2230,38 @@ void EasyThreadNamesWidget::onIdleTimeout()
auto scenePos = mapToScene(mapFromGlobal(QCursor::pos()));
if (scenePos.x() < visibleSceneRect.left() || scenePos.x() > visibleSceneRect.right())
{
if (m_idleTime > 3000)
setFixedWidth(m_maxLength);
return;
}
if (scenePos.y() < visibleSceneRect.top() || scenePos.y() > visibleSceneRect.bottom())
{
if (m_idleTime > 3000)
setFixedWidth(m_maxLength);
return;
}
auto const parentView = static_cast<EasyThreadNamesWidget*>(scene()->parent());
const auto view = parentView->view();
if (scenePos.y() > view->visibleSceneRect().bottom())
{
if (m_idleTime > 3000)
setFixedWidth(m_maxLength);
return;
}
const qreal y = scenePos.y() - visibleSceneRect.top();
const auto& items = view->getItems();
if (items.empty())
{
if (m_idleTime > 3000)
setFixedWidth(m_maxLength);
return;
}
EasyGraphicsItem* intersectingItem = nullptr;
for (auto item : items)
@ -2332,7 +2357,7 @@ void EasyThreadNamesWidget::onIdleTimeout()
auto br = m_popupWidget->boundingRect();
if (m_maxLength < br.width())
if (maximumWidth() < br.width())
{
setFixedWidth(static_cast<int>(br.width()));
visibleSceneRect.setWidth(br.width());

View File

@ -1013,7 +1013,7 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
if (width < MIN_SYNC_SIZE)
width = MIN_SYNC_SIZE;
const ::profiler::thread_id_t tid = EASY_GLOBALS.version < ::profiler_gui::V130 ? item.node->id() : static_cast<const ::profiler::SerializedCSwitch*>(item.node)->tid();
const ::profiler::thread_id_t tid = EASY_GLOBALS.version < ::profiler_gui::V130 ? item.node->id() : item.cs->tid();
const bool self_thread = tid != 0 && EASY_GLOBALS.profiler_blocks.find(tid) != EASY_GLOBALS.profiler_blocks.end();
::profiler::color_t color = 0;