mirror of
https://github.com/yse/easy_profiler.git
synced 2024-12-27 08:41:02 +08:00
First debuggable version of system event tracing for visualizing thread context switches
This commit is contained in:
parent
342a95108c
commit
05b56dcec0
27
include/profiler/event_trace_status.h
Normal file
27
include/profiler/event_trace_status.h
Normal file
@ -0,0 +1,27 @@
|
||||
|
||||
|
||||
|
||||
#ifndef EASY_PROFILER__EVENT_TRACE_STATUS__H_
|
||||
#define EASY_PROFILER__EVENT_TRACE_STATUS__H_
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace profiler {
|
||||
|
||||
enum EventTracingEnableStatus : unsigned char
|
||||
{
|
||||
EVENT_TRACING_LAUNCHED_SUCCESSFULLY = 0,
|
||||
EVENT_TRACING_NOT_ENOUGH_ACCESS_RIGHTS,
|
||||
EVENT_TRACING_WAS_LAUNCHED_BY_SOMEBODY_ELSE,
|
||||
EVENT_TRACING_BAD_PROPERTIES_SIZE,
|
||||
EVENT_TRACING_OPEN_TRACE_ERROR,
|
||||
EVENT_TRACING_MISTERIOUS_ERROR,
|
||||
};
|
||||
|
||||
} // END of namespace profiler.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif // EASY_PROFILER__EVENT_TRACE_STATUS__H_
|
@ -42,7 +42,7 @@ along with this program.If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace profiler {
|
||||
template <bool IS_REF> struct NameSwitch final {
|
||||
template <const bool IS_REF> struct NameSwitch final {
|
||||
static const char* runtime_name(const char* name) { return name; }
|
||||
static const char* compiletime_name(const char*) { return ""; }
|
||||
};
|
||||
@ -217,7 +217,7 @@ namespace profiler {
|
||||
void PROFILER_API beginBlock(Block& _block);
|
||||
void PROFILER_API endBlock();
|
||||
void PROFILER_API setEnabled(bool isEnable);
|
||||
unsigned int PROFILER_API dumpBlocksToFile(const char* filename);
|
||||
uint32_t PROFILER_API dumpBlocksToFile(const char* filename);
|
||||
void PROFILER_API setThreadName(const char* name, const char* filename, const char* _funcname, int line);
|
||||
}
|
||||
|
||||
@ -228,8 +228,8 @@ namespace profiler {
|
||||
enum BlockType : uint8_t
|
||||
{
|
||||
BLOCK_TYPE_EVENT = 0,
|
||||
BLOCK_TYPE_BLOCK,
|
||||
BLOCK_TYPE_THREAD_SIGN,
|
||||
BLOCK_TYPE_BLOCK,
|
||||
BLOCK_TYPE_CONTEXT_SWITCH,
|
||||
|
||||
BLOCK_TYPES_NUMBER
|
||||
@ -264,12 +264,14 @@ namespace profiler {
|
||||
|
||||
BlockDescriptor(uint64_t& _used_mem, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color);
|
||||
|
||||
const char* name() const { return m_name; }
|
||||
const char* file() const { return m_filename; }
|
||||
inline const char* name() const { return m_name; }
|
||||
inline const char* file() const { return m_filename; }
|
||||
};
|
||||
|
||||
class PROFILER_API BaseBlockData
|
||||
{
|
||||
friend ::ProfileManager;
|
||||
|
||||
protected:
|
||||
|
||||
timestamp_t m_begin;
|
||||
@ -283,9 +285,9 @@ namespace profiler {
|
||||
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; }
|
||||
timestamp_t duration() const { return m_end - m_begin; }
|
||||
inline timestamp_t duration() const { return m_end - m_begin; }
|
||||
|
||||
void setId(block_id_t _id) { m_id = _id; }
|
||||
inline void setId(block_id_t _id) { m_id = _id; }
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
@ -298,11 +300,14 @@ namespace profiler {
|
||||
private:
|
||||
|
||||
void finish();
|
||||
void finish(timestamp_t _end_time);
|
||||
inline bool isFinished() const { return m_end >= m_begin; }
|
||||
|
||||
public:
|
||||
|
||||
Block(block_type_t _block_type, block_id_t _id, const char* _name = "");
|
||||
Block(Block&& that);
|
||||
Block(block_type_t _block_type, block_id_t _id, const char* _name);
|
||||
Block(timestamp_t _begin_time, block_type_t _block_type, block_id_t _id, const char* _name);
|
||||
~Block();
|
||||
|
||||
inline const char* name() const { return m_name; }
|
||||
@ -317,7 +322,7 @@ namespace profiler {
|
||||
public:
|
||||
|
||||
StaticBlockDescriptor(const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color = DefaultBlockColor);
|
||||
block_id_t id() const { return m_id; }
|
||||
inline block_id_t id() const { return m_id; }
|
||||
};
|
||||
|
||||
#ifndef _WIN32
|
||||
|
@ -167,6 +167,9 @@ namespace profiler {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define EASY_STORE_CSWITCH_SEPARATELY
|
||||
//#undef EASY_STORE_CSWITCH_SEPARATELY
|
||||
|
||||
class BlocksTreeRoot final
|
||||
{
|
||||
typedef BlocksTreeRoot This;
|
||||
@ -174,6 +177,9 @@ namespace profiler {
|
||||
public:
|
||||
|
||||
BlocksTree::children_t children;
|
||||
#ifdef EASY_STORE_CSWITCH_SEPARATELY
|
||||
BlocksTree::children_t sync;
|
||||
#endif
|
||||
const char* thread_name;
|
||||
::profiler::thread_id_t thread_id;
|
||||
uint16_t depth;
|
||||
@ -182,13 +188,23 @@ namespace profiler {
|
||||
{
|
||||
}
|
||||
|
||||
BlocksTreeRoot(This&& that) : children(::std::move(that.children)), thread_name(that.thread_name), thread_id(that.thread_id), depth(that.depth)
|
||||
BlocksTreeRoot(This&& that)
|
||||
: children(::std::move(that.children))
|
||||
#ifdef EASY_STORE_CSWITCH_SEPARATELY
|
||||
, sync(::std::move(that.sync))
|
||||
#endif
|
||||
, thread_name(that.thread_name)
|
||||
, thread_id(that.thread_id)
|
||||
, depth(that.depth)
|
||||
{
|
||||
}
|
||||
|
||||
This& operator = (This&& that)
|
||||
{
|
||||
children = ::std::move(that.children);
|
||||
#ifdef EASY_STORE_CSWITCH_SEPARATELY
|
||||
sync = ::std::move(that.sync);
|
||||
#endif
|
||||
thread_name = that.thread_name;
|
||||
thread_id = that.thread_id;
|
||||
depth = that.depth;
|
||||
|
@ -76,7 +76,7 @@ const unsigned int TEST_PROGRESSION_BASE = 4;
|
||||
const int FLICKER_INTERVAL = 16; // 60Hz
|
||||
|
||||
const auto CHRONOMETER_FONT = QFont("CourierNew", 16, 2);
|
||||
const auto ITEMS_FONT = QFont("CourierNew", 10);// , 2);
|
||||
const auto ITEMS_FONT = QFont("CourierNew", 9);// , 2);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -95,7 +95,7 @@ inline T logn(T _value)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EasyGraphicsItem::EasyGraphicsItem(unsigned char _index, const::profiler::BlocksTreeRoot* _root)
|
||||
EasyGraphicsItem::EasyGraphicsItem(uint8_t _index, const::profiler::BlocksTreeRoot* _root)
|
||||
: QGraphicsItem(nullptr)
|
||||
, m_pRoot(_root)
|
||||
, m_index(_index)
|
||||
@ -148,7 +148,7 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
|
||||
|
||||
// Reset indices of first visible item for each layer
|
||||
const auto levelsNumber = levels();
|
||||
for (unsigned char i = 1; i < levelsNumber; ++i) ::profiler_gui::set_max(m_levelsIndexes[i]);
|
||||
for (uint8_t i = 1; i < levelsNumber; ++i) ::profiler_gui::set_max(m_levelsIndexes[i]);
|
||||
|
||||
|
||||
// Search for first visible top-level item
|
||||
@ -170,6 +170,24 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
|
||||
}
|
||||
|
||||
|
||||
#ifdef EASY_STORE_CSWITCH_SEPARATELY
|
||||
auto firstSync = ::std::lower_bound(m_pRoot->sync.begin(), m_pRoot->sync.end(), sceneLeft, [&sceneView](::profiler::block_index_t _index, qreal _value)
|
||||
{
|
||||
return sceneView->time2position(easyBlock(_index).tree.node->begin()) < _value;
|
||||
});
|
||||
|
||||
if (firstSync != m_pRoot->sync.end())
|
||||
{
|
||||
if (firstSync != m_pRoot->sync.begin())
|
||||
--firstSync;
|
||||
}
|
||||
else if (!m_pRoot->sync.empty())
|
||||
{
|
||||
firstSync = m_pRoot->sync.begin() + m_pRoot->sync.size() - 1;
|
||||
}
|
||||
firstSync = m_pRoot->sync.begin();
|
||||
#endif
|
||||
|
||||
|
||||
// This is to make _painter->drawText() work properly
|
||||
// (it seems there is a bug in Qt5.6 when drawText called for big coordinates,
|
||||
@ -206,7 +224,7 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
|
||||
// Iterate through layers and draw visible items
|
||||
bool selectedItemsWasPainted = false;
|
||||
const auto visibleBottom = visibleSceneRect.bottom() - 1;
|
||||
for (unsigned char l = 0; l < levelsNumber; ++l)
|
||||
for (uint8_t l = 0; l < levelsNumber; ++l)
|
||||
{
|
||||
auto& level = m_levels[l];
|
||||
const short next_level = l + 1;
|
||||
@ -492,6 +510,46 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef EASY_STORE_CSWITCH_SEPARATELY
|
||||
if (!m_pRoot->sync.empty())
|
||||
{
|
||||
_painter->setBrush(Qt::NoBrush);
|
||||
QPen pen(QColor::fromRgba(0x40f08040));
|
||||
pen.setWidth(3);
|
||||
_painter->setPen(pen);
|
||||
|
||||
qreal prevRight = -1e100, top = y() - 1;
|
||||
for (auto it = firstSync, end = m_pRoot->sync.end(); it != end; ++it)
|
||||
{
|
||||
const auto& item = easyBlock(*it).tree;
|
||||
auto begin = sceneView->time2position(item.node->begin());
|
||||
|
||||
if (begin > sceneRight)
|
||||
break; // This is first totally invisible item. No need to check other items.
|
||||
|
||||
decltype(begin) width = item.node->duration();
|
||||
auto r = begin + width;
|
||||
// if (r < sceneLeft) // This item is not visible
|
||||
// continue;
|
||||
|
||||
begin *= currentScale;
|
||||
begin -= dx;
|
||||
width *= currentScale;
|
||||
// r = begin + width;
|
||||
// if (r <= prevRight) // This item is not visible
|
||||
// continue;
|
||||
|
||||
if (width < 1)
|
||||
width = 1;
|
||||
|
||||
_painter->drawLine(QLineF(begin, top, begin + width, top));
|
||||
prevRight = begin + width;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
_painter->restore();
|
||||
}
|
||||
|
||||
@ -696,17 +754,17 @@ void EasyGraphicsItem::setBoundingRect(const QRectF& _rect)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsigned char EasyGraphicsItem::levels() const
|
||||
uint8_t EasyGraphicsItem::levels() const
|
||||
{
|
||||
return static_cast<unsigned char>(m_levels.size());
|
||||
return static_cast<uint8_t>(m_levels.size());
|
||||
}
|
||||
|
||||
float EasyGraphicsItem::levelY(unsigned char _level) const
|
||||
float EasyGraphicsItem::levelY(uint8_t _level) const
|
||||
{
|
||||
return y() + static_cast<int>(_level) * static_cast<int>(GRAPHICS_ROW_SIZE_FULL);
|
||||
}
|
||||
|
||||
void EasyGraphicsItem::setLevels(unsigned char _levels)
|
||||
void EasyGraphicsItem::setLevels(uint8_t _levels)
|
||||
{
|
||||
typedef decltype(m_levelsIndexes) IndexesT;
|
||||
static const auto MAX_CHILD_INDEX = ::profiler_gui::numeric_max<IndexesT::value_type>();
|
||||
@ -715,29 +773,29 @@ void EasyGraphicsItem::setLevels(unsigned char _levels)
|
||||
m_levelsIndexes.resize(_levels, MAX_CHILD_INDEX);
|
||||
}
|
||||
|
||||
void EasyGraphicsItem::reserve(unsigned char _level, unsigned int _items)
|
||||
void EasyGraphicsItem::reserve(uint8_t _level, unsigned int _items)
|
||||
{
|
||||
m_levels[_level].reserve(_items);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const EasyGraphicsItem::Children& EasyGraphicsItem::items(unsigned char _level) const
|
||||
const EasyGraphicsItem::Children& EasyGraphicsItem::items(uint8_t _level) const
|
||||
{
|
||||
return m_levels[_level];
|
||||
}
|
||||
|
||||
const ::profiler_gui::EasyBlockItem& EasyGraphicsItem::getItem(unsigned char _level, unsigned int _index) const
|
||||
const ::profiler_gui::EasyBlockItem& EasyGraphicsItem::getItem(uint8_t _level, unsigned int _index) const
|
||||
{
|
||||
return m_levels[_level][_index];
|
||||
}
|
||||
|
||||
::profiler_gui::EasyBlockItem& EasyGraphicsItem::getItem(unsigned char _level, unsigned int _index)
|
||||
::profiler_gui::EasyBlockItem& EasyGraphicsItem::getItem(uint8_t _level, unsigned int _index)
|
||||
{
|
||||
return m_levels[_level][_index];
|
||||
}
|
||||
|
||||
unsigned int EasyGraphicsItem::addItem(unsigned char _level)
|
||||
unsigned int EasyGraphicsItem::addItem(uint8_t _level)
|
||||
{
|
||||
m_levels[_level].emplace_back();
|
||||
return static_cast<unsigned int>(m_levels[_level].size() - 1);
|
||||
@ -1194,7 +1252,7 @@ EasyChronometerItem* EasyGraphicsView::createChronometer(bool _main)
|
||||
}
|
||||
}
|
||||
|
||||
void EasyGraphicsView::test(unsigned int _frames_number, unsigned int _total_items_number_estimate, unsigned char _rows)
|
||||
void EasyGraphicsView::test(unsigned int _frames_number, unsigned int _total_items_number_estimate, uint8_t _rows)
|
||||
{
|
||||
static const qreal X_BEGIN = 50;
|
||||
static const qreal Y_BEGIN = 0;
|
||||
@ -1202,9 +1260,9 @@ void EasyGraphicsView::test(unsigned int _frames_number, unsigned int _total_ite
|
||||
clearSilent(); // Clear scene
|
||||
|
||||
// Calculate items number for first level
|
||||
_rows = ::std::max((unsigned char)1, _rows);
|
||||
_rows = ::std::max((uint8_t)1, _rows);
|
||||
const auto children_per_frame = static_cast<unsigned int>(0.5 + static_cast<double>(_total_items_number_estimate) / static_cast<double>(_rows * _frames_number));
|
||||
const unsigned char max_depth = ::std::min(254, static_cast<int>(logn<TEST_PROGRESSION_BASE>(children_per_frame * (TEST_PROGRESSION_BASE - 1) * 0.5 + 1)));
|
||||
const uint8_t max_depth = ::std::min(254, static_cast<int>(logn<TEST_PROGRESSION_BASE>(children_per_frame * (TEST_PROGRESSION_BASE - 1) * 0.5 + 1)));
|
||||
const auto first_level_children_count = static_cast<unsigned int>(static_cast<double>(children_per_frame) * (1.0 - TEST_PROGRESSION_BASE) / (1.0 - pow(TEST_PROGRESSION_BASE, max_depth)) + 0.5);
|
||||
|
||||
|
||||
@ -1213,7 +1271,7 @@ void EasyGraphicsView::test(unsigned int _frames_number, unsigned int _total_ite
|
||||
|
||||
|
||||
::std::vector<EasyGraphicsItem*> thread_items(_rows);
|
||||
for (unsigned char i = 0; i < _rows; ++i)
|
||||
for (uint8_t i = 0; i < _rows; ++i)
|
||||
{
|
||||
auto item = new EasyGraphicsItem(i, true);
|
||||
thread_items[i] = item;
|
||||
@ -1226,9 +1284,9 @@ void EasyGraphicsView::test(unsigned int _frames_number, unsigned int _total_ite
|
||||
|
||||
// Calculate items number for each sublevel
|
||||
auto chldrn = first_level_children_count;
|
||||
for (unsigned char i = 1; i <= max_depth; ++i)
|
||||
for (uint8_t i = 1; i <= max_depth; ++i)
|
||||
{
|
||||
for (unsigned char j = 0; j < _rows; ++j)
|
||||
for (uint8_t j = 0; j < _rows; ++j)
|
||||
{
|
||||
auto item = thread_items[j];
|
||||
item->reserve(i, chldrn * _frames_number);
|
||||
@ -1241,7 +1299,7 @@ void EasyGraphicsView::test(unsigned int _frames_number, unsigned int _total_ite
|
||||
unsigned int total_items = 0;
|
||||
qreal maxX = 0;
|
||||
const EasyGraphicsItem* longestItem = nullptr;
|
||||
for (unsigned char i = 0; i < _rows; ++i)
|
||||
for (uint8_t i = 0; i < _rows; ++i)
|
||||
{
|
||||
auto item = thread_items[i];
|
||||
qreal x = X_BEGIN, y = item->y();
|
||||
@ -1395,7 +1453,7 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr
|
||||
// fill scene with new items
|
||||
const auto& tree = threadTree.second.children;
|
||||
qreal h = 0, x = time2position(blocksTree(tree.front()).node->begin());
|
||||
auto item = new EasyGraphicsItem(static_cast<unsigned char>(m_items.size()), &threadTree.second);
|
||||
auto item = new EasyGraphicsItem(static_cast<uint8_t>(m_items.size()), &threadTree.second);
|
||||
item->setLevels(threadTree.second.depth);
|
||||
item->setPos(0, y);
|
||||
|
||||
@ -1461,7 +1519,7 @@ qreal EasyGraphicsView::setTree(EasyGraphicsItem* _item, const ::profiler::Block
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto level = static_cast<unsigned char>(_level);
|
||||
const auto level = static_cast<uint8_t>(_level);
|
||||
_item->reserve(level, static_cast<unsigned int>(_children.size()));
|
||||
|
||||
const short next_level = _level + 1;
|
||||
@ -1502,7 +1560,7 @@ qreal EasyGraphicsView::setTree(EasyGraphicsItem* _item, const ::profiler::Block
|
||||
|
||||
if (next_level < 256 && next_level < _item->levels() && !child.children.empty())
|
||||
{
|
||||
b.children_begin = static_cast<unsigned int>(_item->items(static_cast<unsigned char>(next_level)).size());
|
||||
b.children_begin = static_cast<unsigned int>(_item->items(static_cast<uint8_t>(next_level)).size());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -67,11 +67,11 @@ class EasyGraphicsItem : public QGraphicsItem
|
||||
|
||||
QRectF m_boundingRect; ///< boundingRect (see QGraphicsItem)
|
||||
const ::profiler::BlocksTreeRoot* m_pRoot; ///< Pointer to the root profiler block (thread block). Used by ProfTreeWidget to restore hierarchy.
|
||||
unsigned char m_index; ///< This item's index in the list of items of EasyGraphicsView
|
||||
uint8_t m_index; ///< This item's index in the list of items of EasyGraphicsView
|
||||
|
||||
public:
|
||||
|
||||
EasyGraphicsItem(unsigned char _index, const::profiler::BlocksTreeRoot* _root);
|
||||
EasyGraphicsItem(uint8_t _index, const::profiler::BlocksTreeRoot* _root);
|
||||
virtual ~EasyGraphicsItem();
|
||||
|
||||
// Public virtual methods
|
||||
@ -92,46 +92,46 @@ public:
|
||||
::profiler::thread_id_t threadId() const;
|
||||
|
||||
///< Returns number of levels
|
||||
unsigned char levels() const;
|
||||
uint8_t levels() const;
|
||||
|
||||
float levelY(unsigned char _level) const;
|
||||
float levelY(uint8_t _level) const;
|
||||
|
||||
/** \brief Sets number of levels.
|
||||
|
||||
\note Must be set before doing anything else.
|
||||
|
||||
\param _levels Desired number of levels */
|
||||
void setLevels(unsigned char _levels);
|
||||
void setLevels(uint8_t _levels);
|
||||
|
||||
/** \brief Reserves memory for desired number of items on specified level.
|
||||
|
||||
\param _level Index of the level
|
||||
\param _items Desired number of items on this level */
|
||||
void reserve(unsigned char _level, unsigned int _items);
|
||||
void reserve(uint8_t _level, unsigned int _items);
|
||||
|
||||
/**\brief Returns reference to the array of items of specified level.
|
||||
|
||||
\param _level Index of the level */
|
||||
const Children& items(unsigned char _level) const;
|
||||
const Children& items(uint8_t _level) const;
|
||||
|
||||
/**\brief Returns reference to the item with required index on specified level.
|
||||
|
||||
\param _level Index of the level
|
||||
\param _index Index of required item */
|
||||
const ::profiler_gui::EasyBlockItem& getItem(unsigned char _level, unsigned int _index) const;
|
||||
const ::profiler_gui::EasyBlockItem& getItem(uint8_t _level, unsigned int _index) const;
|
||||
|
||||
/**\brief Returns reference to the item with required index on specified level.
|
||||
|
||||
\param _level Index of the level
|
||||
\param _index Index of required item */
|
||||
::profiler_gui::EasyBlockItem& getItem(unsigned char _level, unsigned int _index);
|
||||
::profiler_gui::EasyBlockItem& getItem(uint8_t _level, unsigned int _index);
|
||||
|
||||
/** \brief Adds new item to required level.
|
||||
|
||||
\param _level Index of the level
|
||||
|
||||
\retval Index of the new created item */
|
||||
unsigned int addItem(unsigned char _level);
|
||||
unsigned int addItem(uint8_t _level);
|
||||
|
||||
/** \brief Finds top-level blocks which are intersects with required selection zone.
|
||||
|
||||
@ -154,7 +154,7 @@ public:
|
||||
// Public inline methods
|
||||
|
||||
///< Returns this item's index in the list of graphics items of EasyGraphicsView
|
||||
inline unsigned char index() const
|
||||
inline uint8_t index() const
|
||||
{
|
||||
return m_index;
|
||||
}
|
||||
@ -363,7 +363,7 @@ public:
|
||||
return m_timelineStep;
|
||||
}
|
||||
|
||||
private:
|
||||
//private:
|
||||
|
||||
// Private inline methods
|
||||
|
||||
|
@ -4,13 +4,16 @@ set(CPP_FILES
|
||||
block.cpp
|
||||
profile_manager.cpp
|
||||
reader.cpp
|
||||
event_trace_win.cpp
|
||||
)
|
||||
|
||||
set(H_FILES
|
||||
${ROOT}/include/profiler/profiler.h
|
||||
${ROOT}/include/profiler/reader.h
|
||||
${ROOT}/include/profiler/event_trace_status.h
|
||||
profile_manager.h
|
||||
spin_lock.h
|
||||
event_trace_win.h
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
|
@ -7,10 +7,7 @@
|
||||
using namespace profiler;
|
||||
|
||||
#ifdef _WIN32
|
||||
struct ProfPerformanceFrequency {
|
||||
LARGE_INTEGER frequency;
|
||||
ProfPerformanceFrequency() { QueryPerformanceFrequency(&frequency); }
|
||||
} const WINDOWS_CPU_INFO;
|
||||
decltype(LARGE_INTEGER::QuadPart) CPU_FREQUENCY = ([](){ LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); return freq.QuadPart; })();
|
||||
#endif
|
||||
|
||||
inline timestamp_t getCurrentTime()
|
||||
@ -20,8 +17,8 @@ inline timestamp_t getCurrentTime()
|
||||
LARGE_INTEGER elapsedMicroseconds;
|
||||
if (!QueryPerformanceCounter(&elapsedMicroseconds))
|
||||
return 0;
|
||||
elapsedMicroseconds.QuadPart *= 1000000000LL;
|
||||
elapsedMicroseconds.QuadPart /= WINDOWS_CPU_INFO.frequency.QuadPart;
|
||||
//elapsedMicroseconds.QuadPart *= 1000000000LL;
|
||||
//elapsedMicroseconds.QuadPart /= CPU_FREQUENCY;
|
||||
return (timestamp_t)elapsedMicroseconds.QuadPart;
|
||||
#else
|
||||
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> time_point;
|
||||
@ -38,11 +35,23 @@ BaseBlockData::BaseBlockData(timestamp_t _begin_time, block_id_t _descriptor_id)
|
||||
|
||||
}
|
||||
|
||||
Block::Block(Block&& that)
|
||||
: BaseBlockData(that.m_begin, that.m_id)
|
||||
, m_name(that.m_name)
|
||||
{
|
||||
m_end = that.m_end;
|
||||
}
|
||||
|
||||
Block::Block(block_type_t _block_type, block_id_t _descriptor_id, const char* _name)
|
||||
: BaseBlockData(getCurrentTime(), _descriptor_id)
|
||||
: Block(getCurrentTime(), _block_type, _descriptor_id, _name)
|
||||
{
|
||||
}
|
||||
|
||||
Block::Block(timestamp_t _begin_time, block_type_t _block_type, block_id_t _descriptor_id, const char* _name)
|
||||
: BaseBlockData(_begin_time, _descriptor_id)
|
||||
, m_name(_name)
|
||||
{
|
||||
if (_block_type != BLOCK_TYPE_BLOCK)
|
||||
if (static_cast<uint8_t>(_block_type) < BLOCK_TYPE_BLOCK)
|
||||
{
|
||||
m_end = m_begin;
|
||||
}
|
||||
@ -53,6 +62,11 @@ void Block::finish()
|
||||
m_end = getCurrentTime();
|
||||
}
|
||||
|
||||
void Block::finish(timestamp_t _end_time)
|
||||
{
|
||||
m_end = _end_time;
|
||||
}
|
||||
|
||||
Block::~Block()
|
||||
{
|
||||
if (!isFinished())
|
||||
|
197
src/event_trace_win.cpp
Normal file
197
src/event_trace_win.cpp
Normal file
@ -0,0 +1,197 @@
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <memory.h>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include "event_trace_win.h"
|
||||
#include "profiler/profiler.h"
|
||||
#include "profile_manager.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
extern decltype(LARGE_INTEGER::QuadPart) CPU_FREQUENCY;
|
||||
//extern ProfileManager& MANAGER;
|
||||
#define MANAGER ProfileManager::instance()
|
||||
|
||||
namespace profiler {
|
||||
|
||||
struct CSwitch final
|
||||
{
|
||||
uint32_t NewThreadId;
|
||||
uint32_t OldThreadId;
|
||||
int8_t NewThreadPriority;
|
||||
int8_t OldThreadPriority;
|
||||
uint8_t PreviousCState;
|
||||
int8_t SpareByte;
|
||||
int8_t OldThreadWaitReason;
|
||||
int8_t OldThreadWaitMode;
|
||||
int8_t OldThreadState;
|
||||
int8_t OldThreadWaitIdealProcessor;
|
||||
uint32_t NewThreadWaitTime;
|
||||
uint32_t Reserved;
|
||||
};
|
||||
|
||||
void WINAPI processTraceEvent(PEVENT_RECORD _traceEvent)
|
||||
{
|
||||
//static ::std::ofstream outputFile("csw.csv", ::std::fstream::app);
|
||||
|
||||
static const decltype(_traceEvent->EventHeader.EventDescriptor.Opcode) SWITCH_CONTEXT_OPCODE = 36;
|
||||
if (_traceEvent->EventHeader.EventDescriptor.Opcode != SWITCH_CONTEXT_OPCODE)
|
||||
return;
|
||||
|
||||
if (sizeof(CSwitch) != _traceEvent->UserDataLength)
|
||||
return;
|
||||
|
||||
auto _contextSwitchEvent = reinterpret_cast<CSwitch*>(_traceEvent->UserData);
|
||||
|
||||
auto timestampValue = _traceEvent->EventHeader.TimeStamp.QuadPart;
|
||||
|
||||
//LARGE_INTEGER currtime;
|
||||
//if (QueryPerformanceCounter(&currtime))
|
||||
// timestampValue = currtime.QuadPart;
|
||||
|
||||
//timestampValue *= 1000000000LL;
|
||||
//timestampValue /= CPU_FREQUENCY;
|
||||
const auto time = (::profiler::timestamp_t)timestampValue;
|
||||
|
||||
static const ::profiler::StaticBlockDescriptor desc("OS.ContextSwitch", __FILE__, __LINE__, ::profiler::BLOCK_TYPE_CONTEXT_SWITCH, ::profiler::colors::White);
|
||||
|
||||
//if (_contextSwitchEvent->OldThreadId != 0)
|
||||
MANAGER._cswitchBeginBlock(time, desc.id(), _contextSwitchEvent->OldThreadId);
|
||||
|
||||
//if (_contextSwitchEvent->NewThreadId != 0)
|
||||
MANAGER._cswitchEndBlock(_contextSwitchEvent->NewThreadId, time);
|
||||
|
||||
//static const auto firstTime = time;
|
||||
//outputFile << _contextSwitchEvent->OldThreadId << "\t" << _contextSwitchEvent->NewThreadId << "\t" << (((time - firstTime) * 1000000000LL) / CPU_FREQUENCY) << ::std::endl;
|
||||
//outputFile << _contextSwitchEvent->OldThreadId << "\t" << _contextSwitchEvent->NewThreadId << "\t" << time << ::std::endl;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EasyEventTracer& EasyEventTracer::instance()
|
||||
{
|
||||
static EasyEventTracer tracer;
|
||||
return tracer;
|
||||
}
|
||||
|
||||
EasyEventTracer::EasyEventTracer()
|
||||
{
|
||||
}
|
||||
|
||||
EasyEventTracer::~EasyEventTracer()
|
||||
{
|
||||
disable();
|
||||
}
|
||||
|
||||
::profiler::EventTracingEnableStatus EasyEventTracer::startTrace(bool _force, int _step)
|
||||
{
|
||||
auto startTraceResult = StartTrace(&m_sessionHandle, KERNEL_LOGGER_NAME, props());
|
||||
switch (startTraceResult)
|
||||
{
|
||||
case ERROR_SUCCESS:
|
||||
return EVENT_TRACING_LAUNCHED_SUCCESSFULLY;
|
||||
|
||||
case ERROR_ALREADY_EXISTS:
|
||||
{
|
||||
if (_force)
|
||||
{
|
||||
// Try to stop another event tracing session to force launch self session.
|
||||
|
||||
if ((_step == 0 && 32 < (int)ShellExecute(NULL, NULL, "logman", "stop \"" KERNEL_LOGGER_NAME "\" -ets", NULL, SW_HIDE)) || (_step > 0 && _step < 6))
|
||||
{
|
||||
// Command executed successfully. Wait for a few time until tracing session finish.
|
||||
::std::this_thread::sleep_for(::std::chrono::milliseconds(500));
|
||||
return startTrace(true, ++_step);
|
||||
}
|
||||
}
|
||||
|
||||
return EVENT_TRACING_WAS_LAUNCHED_BY_SOMEBODY_ELSE;
|
||||
}
|
||||
|
||||
case ERROR_ACCESS_DENIED:
|
||||
return EVENT_TRACING_NOT_ENOUGH_ACCESS_RIGHTS;
|
||||
|
||||
case ERROR_BAD_LENGTH:
|
||||
return EVENT_TRACING_BAD_PROPERTIES_SIZE;
|
||||
}
|
||||
|
||||
return EVENT_TRACING_MISTERIOUS_ERROR;
|
||||
}
|
||||
|
||||
::profiler::EventTracingEnableStatus EasyEventTracer::enable(bool _force)
|
||||
{
|
||||
static const decltype(m_properties.base.Wnode.ClientContext) RAW_TIMESTAMP_TIME_TYPE = 1;
|
||||
|
||||
if (m_bEnabled)
|
||||
return EVENT_TRACING_LAUNCHED_SUCCESSFULLY;
|
||||
|
||||
// Clear properties
|
||||
memset(&m_properties, 0, sizeof(m_properties));
|
||||
m_properties.base.Wnode.BufferSize = sizeof(m_properties);
|
||||
m_properties.base.Wnode.Flags = WNODE_FLAG_TRACED_GUID;
|
||||
m_properties.base.Wnode.ClientContext = RAW_TIMESTAMP_TIME_TYPE;
|
||||
m_properties.base.Wnode.Guid = SystemTraceControlGuid;
|
||||
m_properties.base.LoggerNameOffset = sizeof(m_properties.base);
|
||||
m_properties.base.EnableFlags = EVENT_TRACE_FLAG_CSWITCH;
|
||||
m_properties.base.LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
|
||||
|
||||
auto res = startTrace(_force);
|
||||
if (res != EVENT_TRACING_LAUNCHED_SUCCESSFULLY)
|
||||
return res;
|
||||
|
||||
memset(&m_trace, 0, sizeof(m_trace));
|
||||
m_trace.LoggerName = KERNEL_LOGGER_NAME;
|
||||
m_trace.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD | PROCESS_TRACE_MODE_RAW_TIMESTAMP;
|
||||
m_trace.EventRecordCallback = ::profiler::processTraceEvent;
|
||||
|
||||
m_openedHandle = OpenTrace(&m_trace);
|
||||
if (m_openedHandle == INVALID_PROCESSTRACE_HANDLE)
|
||||
return EVENT_TRACING_OPEN_TRACE_ERROR;
|
||||
|
||||
// Have to launch a thread to process events because according to MSDN documentation:
|
||||
//
|
||||
// The ProcessTrace function blocks the thread until it delivers all events, the BufferCallback function returns FALSE,
|
||||
// or you call CloseTrace. If the consumer is consuming events in real time, the ProcessTrace function returns after
|
||||
// the controller stops the trace session. (Note that there may be a several-second delay before the function returns.)
|
||||
//
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa364093(v=vs.85).aspx
|
||||
m_stubThread = ::std::move(::std::thread([this]()
|
||||
{
|
||||
// EASY_THREAD("ProcessTrace");
|
||||
// EASY_BLOCK("ProcessTrace", ::profiler::colors::Red);
|
||||
ProcessTrace(&m_openedHandle, 1, 0, 0);
|
||||
}));
|
||||
|
||||
m_bEnabled = true;
|
||||
|
||||
return EVENT_TRACING_LAUNCHED_SUCCESSFULLY;
|
||||
}
|
||||
|
||||
void EasyEventTracer::disable()
|
||||
{
|
||||
if (!m_bEnabled)
|
||||
return;
|
||||
|
||||
auto controlTraceResult = ControlTrace(m_openedHandle, KERNEL_LOGGER_NAME, props(), EVENT_TRACE_CONTROL_STOP);
|
||||
|
||||
// ERROR_CTX_CLOSE_PENDING(7007L): The call was successful. The ProcessTrace function will stop after it has processed all real-time events in its buffers (it will not receive any new events).
|
||||
// ERROR_BUSY(170L): Prior to Windows Vista, you cannot close the trace until the ProcessTrace function completes.
|
||||
// ERROR_INVALID_HANDLE(6L): One of the following is true: TraceHandle is NULL. TraceHandle is INVALID_HANDLE_VALUE.
|
||||
auto closeTraceStatus = CloseTrace(m_openedHandle);
|
||||
|
||||
// Wait for ProcessThread to finish
|
||||
if (m_stubThread.joinable())
|
||||
m_stubThread.join();
|
||||
|
||||
m_bEnabled = false;
|
||||
}
|
||||
|
||||
} // END of namespace profiler.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif // _WIN32
|
66
src/event_trace_win.h
Normal file
66
src/event_trace_win.h
Normal file
@ -0,0 +1,66 @@
|
||||
|
||||
|
||||
|
||||
#ifndef EASY_PROFILER__EVENT_TRACE_WINDOWS__H_
|
||||
#define EASY_PROFILER__EVENT_TRACE_WINDOWS__H_
|
||||
#ifdef _WIN32
|
||||
|
||||
#define INITGUID // This is to enable using SystemTraceControlGuid in evntrace.h.
|
||||
#include <Windows.h>
|
||||
//#include <Strsafe.h>
|
||||
#include <wmistr.h>
|
||||
#include <evntrace.h>
|
||||
#include <evntcons.h>
|
||||
#include <thread>
|
||||
#include "profiler/event_trace_status.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace profiler {
|
||||
|
||||
class EasyEventTracer final
|
||||
{
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct Properties final {
|
||||
EVENT_TRACE_PROPERTIES base;
|
||||
char sessionName[sizeof(KERNEL_LOGGER_NAME)];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
::std::thread m_stubThread;
|
||||
Properties m_properties;
|
||||
EVENT_TRACE_LOGFILE m_trace;
|
||||
TRACEHANDLE m_sessionHandle = INVALID_PROCESSTRACE_HANDLE;
|
||||
TRACEHANDLE m_openedHandle = INVALID_PROCESSTRACE_HANDLE;
|
||||
bool m_bEnabled = false;
|
||||
|
||||
public:
|
||||
|
||||
static EasyEventTracer& instance();
|
||||
~EasyEventTracer();
|
||||
|
||||
::profiler::EventTracingEnableStatus enable(bool _force = false);
|
||||
void disable();
|
||||
|
||||
private:
|
||||
|
||||
EasyEventTracer();
|
||||
|
||||
inline EVENT_TRACE_PROPERTIES* props()
|
||||
{
|
||||
return reinterpret_cast<EVENT_TRACE_PROPERTIES*>(&m_properties);
|
||||
}
|
||||
|
||||
::profiler::EventTracingEnableStatus startTrace(bool _force, int _step = 0);
|
||||
|
||||
}; // END of class EasyEventTracer.
|
||||
|
||||
} // END of namespace profiler.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif // _WIN32
|
||||
#endif // EASY_PROFILER__EVENT_TRACE_WINDOWS__H_
|
@ -1,5 +1,6 @@
|
||||
#include "profile_manager.h"
|
||||
#include "profiler/serialized_block.h"
|
||||
#include "event_trace_win.h"
|
||||
|
||||
#include <thread>
|
||||
#include <string.h>
|
||||
@ -8,7 +9,12 @@
|
||||
|
||||
using namespace profiler;
|
||||
|
||||
auto& MANAGER = ProfileManager::instance();
|
||||
#ifdef _WIN32
|
||||
extern decltype(LARGE_INTEGER::QuadPart) CPU_FREQUENCY;
|
||||
#endif
|
||||
|
||||
//auto& MANAGER = ProfileManager::instance();
|
||||
#define MANAGER ProfileManager::instance()
|
||||
|
||||
extern "C"{
|
||||
|
||||
@ -20,13 +26,18 @@ extern "C"{
|
||||
void PROFILER_API setEnabled(bool isEnable)
|
||||
{
|
||||
MANAGER.setEnabled(isEnable);
|
||||
if (isEnable)
|
||||
EasyEventTracer::instance().enable(true);
|
||||
else
|
||||
EasyEventTracer::instance().disable();
|
||||
}
|
||||
|
||||
void PROFILER_API beginBlock(Block& _block)
|
||||
{
|
||||
MANAGER.beginBlock(_block);
|
||||
}
|
||||
|
||||
unsigned int PROFILER_API dumpBlocksToFile(const char* filename)
|
||||
uint32_t PROFILER_API dumpBlocksToFile(const char* filename)
|
||||
{
|
||||
return MANAGER.dumpBlocksToFile(filename);
|
||||
}
|
||||
@ -35,6 +46,7 @@ extern "C"{
|
||||
{
|
||||
return MANAGER.setThreadName(name, filename, _funcname, line);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SerializedBlock::SerializedBlock(const Block& block, uint16_t name_length)
|
||||
@ -71,28 +83,53 @@ BlockDescriptor::BlockDescriptor(uint64_t& _used_mem, const char* _name, const c
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ThreadStorage::store(const profiler::Block& block)
|
||||
void ThreadStorage::storeBlock(const profiler::Block& block)
|
||||
{
|
||||
auto name_length = static_cast<uint16_t>(strlen(block.name()));
|
||||
auto size = static_cast<uint16_t>(sizeof(BaseBlockData) + name_length + 1);
|
||||
auto data = m_allocator.allocate(size);
|
||||
auto data = blocks.alloc.allocate(size);
|
||||
::new (static_cast<void*>(data)) SerializedBlock(block, name_length);
|
||||
usedMemorySize += size;
|
||||
closedList.emplace_back(reinterpret_cast<SerializedBlock*>(data));
|
||||
blocks.usedMemorySize += size;
|
||||
blocks.closedList.emplace_back(reinterpret_cast<SerializedBlock*>(data));
|
||||
}
|
||||
|
||||
void ThreadStorage::storeCSwitch(const profiler::Block& block)
|
||||
{
|
||||
auto name_length = static_cast<uint16_t>(strlen(block.name()));
|
||||
auto size = static_cast<uint16_t>(sizeof(BaseBlockData) + name_length + 1);
|
||||
auto data = sync.alloc.allocate(size);
|
||||
::new (static_cast<void*>(data)) SerializedBlock(block, name_length);
|
||||
sync.usedMemorySize += size;
|
||||
sync.closedList.emplace_back(reinterpret_cast<SerializedBlock*>(data));
|
||||
}
|
||||
|
||||
void ThreadStorage::clearClosed()
|
||||
{
|
||||
serialized_list_t().swap(closedList);
|
||||
m_allocator.clear();
|
||||
usedMemorySize = 0;
|
||||
blocks.clearClosed();
|
||||
sync.clearClosed();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// #ifdef _WIN32
|
||||
// LPTOP_LEVEL_EXCEPTION_FILTER PREVIOUS_FILTER = NULL;
|
||||
// LONG WINAPI easyTopLevelExceptionFilter(struct _EXCEPTION_POINTERS* ExceptionInfo)
|
||||
// {
|
||||
// std::ofstream testexp("TEST_EXP.txt", std::fstream::binary);
|
||||
// testexp.write("APPLICATION CRASHED!", strlen("APPLICATION CRASHED!"));
|
||||
//
|
||||
// EasyEventTracer::instance().disable();
|
||||
// if (PREVIOUS_FILTER)
|
||||
// return PREVIOUS_FILTER(ExceptionInfo);
|
||||
// return EXCEPTION_CONTINUE_SEARCH;
|
||||
// }
|
||||
// #endif
|
||||
|
||||
ProfileManager::ProfileManager()
|
||||
{
|
||||
|
||||
// #ifdef _WIN32
|
||||
// PREVIOUS_FILTER = SetUnhandledExceptionFilter(easyTopLevelExceptionFilter);
|
||||
// #endif
|
||||
}
|
||||
|
||||
ProfileManager::~ProfileManager()
|
||||
@ -116,9 +153,16 @@ void ProfileManager::beginBlock(Block& _block)
|
||||
auto& thread_storage = threadStorage(getCurrentThreadId());
|
||||
|
||||
if (!_block.isFinished())
|
||||
thread_storage.openedList.emplace(_block);
|
||||
thread_storage.blocks.openedList.emplace(_block);
|
||||
else
|
||||
thread_storage.store(_block);
|
||||
thread_storage.storeBlock(_block);
|
||||
}
|
||||
|
||||
void ProfileManager::_cswitchBeginBlock(profiler::timestamp_t _time, profiler::block_id_t _id, profiler::thread_id_t _thread_id)
|
||||
{
|
||||
auto thread_storage = _threadStorage(_thread_id);
|
||||
if (thread_storage != nullptr)
|
||||
thread_storage->sync.openedList.emplace(_time, profiler::BLOCK_TYPE_CONTEXT_SWITCH, _id, "");
|
||||
}
|
||||
|
||||
void ProfileManager::endBlock()
|
||||
@ -127,15 +171,28 @@ void ProfileManager::endBlock()
|
||||
return;
|
||||
|
||||
auto& thread_storage = threadStorage(getCurrentThreadId());
|
||||
if (thread_storage.openedList.empty())
|
||||
if (thread_storage.blocks.openedList.empty())
|
||||
return;
|
||||
|
||||
Block& lastBlock = thread_storage.openedList.top();
|
||||
Block& lastBlock = thread_storage.blocks.openedList.top();
|
||||
if (!lastBlock.isFinished())
|
||||
lastBlock.finish();
|
||||
|
||||
thread_storage.store(lastBlock);
|
||||
thread_storage.openedList.pop();
|
||||
thread_storage.storeBlock(lastBlock);
|
||||
thread_storage.blocks.openedList.pop();
|
||||
}
|
||||
|
||||
void ProfileManager::_cswitchEndBlock(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime)
|
||||
{
|
||||
auto thread_storage = _threadStorage(_thread_id);
|
||||
if (thread_storage == nullptr || thread_storage->sync.openedList.empty())
|
||||
return;
|
||||
|
||||
Block& lastBlock = thread_storage->sync.openedList.top();
|
||||
lastBlock.finish(_endtime);
|
||||
|
||||
thread_storage->storeCSwitch(lastBlock);
|
||||
thread_storage->sync.openedList.pop();
|
||||
}
|
||||
|
||||
void ProfileManager::setEnabled(bool isEnable)
|
||||
@ -160,22 +217,42 @@ public:
|
||||
template <class T> void write(const T& _data) {
|
||||
m_file.write((const char*)&_data, sizeof(T));
|
||||
}
|
||||
|
||||
void writeBlock(const profiler::SerializedBlock* _block)
|
||||
{
|
||||
auto sz = static_cast<uint16_t>(sizeof(BaseBlockData) + strlen(_block->name()) + 1);
|
||||
write(sz);
|
||||
write(_block->data(), sz);
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define STORE_CSWITCHES_SEPARATELY
|
||||
|
||||
uint32_t ProfileManager::dumpBlocksToFile(const char* filename)
|
||||
{
|
||||
const bool wasEnabled = m_isEnabled;
|
||||
if (wasEnabled)
|
||||
::profiler::setEnabled(false);
|
||||
|
||||
FileWriter of(filename);
|
||||
|
||||
uint64_t usedMemorySize = 0;
|
||||
uint32_t blocks_number = 0;
|
||||
for (const auto& thread_storage : m_threads)
|
||||
{
|
||||
usedMemorySize += thread_storage.second.usedMemorySize;
|
||||
blocks_number += static_cast<uint32_t>(thread_storage.second.closedList.size());
|
||||
const auto& t = thread_storage.second;
|
||||
usedMemorySize += t.blocks.usedMemorySize + t.sync.usedMemorySize;
|
||||
blocks_number += static_cast<uint32_t>(t.blocks.closedList.size()) + static_cast<uint32_t>(t.sync.closedList.size());
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
of.write(CPU_FREQUENCY);
|
||||
#else
|
||||
of.write(0LL);
|
||||
#endif
|
||||
|
||||
of.write(blocks_number);
|
||||
of.write(usedMemorySize);
|
||||
of.write(static_cast<uint32_t>(m_descriptors.size()));
|
||||
@ -194,21 +271,64 @@ uint32_t ProfileManager::dumpBlocksToFile(const char* filename)
|
||||
of.write(descriptor.file(), filename_size);
|
||||
}
|
||||
|
||||
//::std::ofstream outputFile("csw2.csv", ::std::fstream::app);
|
||||
|
||||
for (auto& thread_storage : m_threads)
|
||||
{
|
||||
of.write(thread_storage.first);
|
||||
of.write(static_cast<uint32_t>(thread_storage.second.closedList.size()));
|
||||
auto& t = thread_storage.second;
|
||||
|
||||
for (auto b : thread_storage.second.closedList)
|
||||
//outputFile << thread_storage.first << std::endl;
|
||||
|
||||
of.write(thread_storage.first);
|
||||
#ifdef STORE_CSWITCHES_SEPARATELY
|
||||
of.write(static_cast<uint32_t>(t.blocks.closedList.size()));
|
||||
#else
|
||||
of.write(static_cast<uint32_t>(t.blocks.closedList.size()) + static_cast<uint32_t>(t.sync.closedList.size()));
|
||||
uint32_t i = 0;
|
||||
#endif
|
||||
|
||||
for (auto b : t.blocks.closedList)
|
||||
{
|
||||
auto sz = static_cast<uint16_t>(sizeof(BaseBlockData) + strlen(b->name()) + 1);
|
||||
of.write(sz);
|
||||
of.write(b->data(), sz);
|
||||
#ifndef STORE_CSWITCHES_SEPARATELY
|
||||
if (i < t.sync.closedList.size())
|
||||
{
|
||||
auto s = t.sync.closedList[i];
|
||||
if (s->end() <= b->end())// || s->begin() >= b->begin())
|
||||
//if (((s->end() <= b->end() && s->end() >= b->begin()) || (s->begin() >= b->begin() && s->begin() <= b->end())))
|
||||
{
|
||||
if (s->m_begin < b->m_begin)
|
||||
s->m_begin = b->m_begin;
|
||||
if (s->m_end > b->m_end)
|
||||
s->m_end = b->m_end;
|
||||
of.writeBlock(s);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
of.writeBlock(b);
|
||||
}
|
||||
|
||||
thread_storage.second.clearClosed();
|
||||
#ifdef STORE_CSWITCHES_SEPARATELY
|
||||
of.write(static_cast<uint32_t>(t.sync.closedList.size()));
|
||||
for (auto b : t.sync.closedList)
|
||||
{
|
||||
#else
|
||||
for (; i < t.sync.closedList.size(); ++i)
|
||||
{
|
||||
auto b = t.sync.closedList[i];
|
||||
#endif
|
||||
//outputFile << b->begin() << "\t" << b->end() << std::endl;
|
||||
|
||||
of.writeBlock(b);
|
||||
}
|
||||
|
||||
t.clearClosed();
|
||||
}
|
||||
|
||||
// if (wasEnabled)
|
||||
// ::profiler::setEnabled(true);
|
||||
|
||||
return blocks_number;
|
||||
}
|
||||
|
||||
@ -219,7 +339,7 @@ void ProfileManager::setThreadName(const char* name, const char* filename, const
|
||||
return;
|
||||
|
||||
const auto id = addBlockDescriptor(_funcname, filename, line, profiler::BLOCK_TYPE_THREAD_SIGN, profiler::colors::Random);
|
||||
thread_storage.store(profiler::Block(profiler::BLOCK_TYPE_THREAD_SIGN, id, name));
|
||||
thread_storage.storeBlock(profiler::Block(profiler::BLOCK_TYPE_THREAD_SIGN, id, name));
|
||||
thread_storage.named = true;
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ namespace profiler { class SerializedBlock; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class T, uint16_t N>
|
||||
template <class T, const uint16_t N>
|
||||
class chunk_allocator final
|
||||
{
|
||||
struct chunk { T data[N]; };
|
||||
@ -91,27 +91,41 @@ public:
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class ThreadStorage
|
||||
class ThreadStorage final
|
||||
{
|
||||
typedef std::stack<std::reference_wrapper<profiler::Block> > stack_of_blocks_t;
|
||||
typedef std::vector<profiler::SerializedBlock*> serialized_list_t;
|
||||
|
||||
chunk_allocator<char, 1024> m_allocator;
|
||||
template <class T, const uint16_t N>
|
||||
struct BlocksList final
|
||||
{
|
||||
typedef std::stack<T> stack_of_blocks_t;
|
||||
|
||||
chunk_allocator<char, N> alloc;
|
||||
stack_of_blocks_t openedList;
|
||||
serialized_list_t closedList;
|
||||
uint64_t usedMemorySize = 0;
|
||||
|
||||
void clearClosed() {
|
||||
serialized_list_t().swap(closedList);
|
||||
alloc.clear();
|
||||
usedMemorySize = 0;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
stack_of_blocks_t openedList;
|
||||
serialized_list_t closedList;
|
||||
uint64_t usedMemorySize = 0;
|
||||
bool named = false;
|
||||
BlocksList<std::reference_wrapper<profiler::Block>, 1024> blocks;
|
||||
BlocksList<profiler::Block, 1024> sync;
|
||||
bool named = false;
|
||||
|
||||
void store(const profiler::Block& _block);
|
||||
void storeBlock(const profiler::Block& _block);
|
||||
void storeCSwitch(const profiler::Block& _block);
|
||||
void clearClosed();
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class ProfileManager
|
||||
class ProfileManager final
|
||||
{
|
||||
friend profiler::StaticBlockDescriptor;
|
||||
|
||||
@ -141,6 +155,9 @@ public:
|
||||
uint32_t dumpBlocksToFile(const char* filename);
|
||||
void setThreadName(const char* name, const char* filename, const char* _funcname, int line);
|
||||
|
||||
void _cswitchBeginBlock(profiler::timestamp_t _time, profiler::block_id_t _id, profiler::thread_id_t _thread_id);
|
||||
void _cswitchEndBlock(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime);
|
||||
|
||||
private:
|
||||
|
||||
template <class ... TArgs>
|
||||
@ -157,6 +174,13 @@ private:
|
||||
guard_lock_t lock(m_spin);
|
||||
return m_threads[_thread_id];
|
||||
}
|
||||
|
||||
ThreadStorage* _threadStorage(profiler::thread_id_t _thread_id)
|
||||
{
|
||||
guard_lock_t lock(m_spin);
|
||||
auto it = m_threads.find(_thread_id);
|
||||
return it != m_threads.end() ? &it->second : nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
110
src/reader.cpp
110
src/reader.cpp
@ -254,7 +254,7 @@ void update_statistics_recursive(StatsMap& _stats_map, ::profiler::BlocksTree& _
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic<int>& progress, const char* filename, ::profiler::SerializedData& serialized_blocks, ::profiler::SerializedData& serialized_descriptors, ::profiler::descriptors_list_t& descriptors, ::profiler::blocks_t& _blocks, ::profiler::thread_blocks_tree_t& threaded_trees, bool gather_statistics)
|
||||
extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic<int>& progress, const char* filename, ::profiler::SerializedData& serialized_blocks, ::profiler::SerializedData& serialized_descriptors, ::profiler::descriptors_list_t& descriptors, ::profiler::blocks_t& blocks, ::profiler::thread_blocks_tree_t& threaded_trees, bool gather_statistics)
|
||||
{
|
||||
EASY_FUNCTION(::profiler::colors::Cyan);
|
||||
|
||||
@ -264,6 +264,9 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic<int>& progr
|
||||
if (!inFile.is_open())
|
||||
return 0;
|
||||
|
||||
int64_t cpu_frequency = 0LL;
|
||||
inFile.read((char*)&cpu_frequency, sizeof(int64_t));
|
||||
|
||||
uint32_t total_blocks_number = 0;
|
||||
inFile.read((char*)&total_blocks_number, sizeof(decltype(total_blocks_number)));
|
||||
if (total_blocks_number == 0)
|
||||
@ -321,7 +324,7 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic<int>& progr
|
||||
i = 0;
|
||||
uint32_t read_number = 0;
|
||||
::profiler::block_index_t blocks_counter = 0;
|
||||
_blocks.reserve(total_blocks_number);
|
||||
blocks.reserve(total_blocks_number);
|
||||
while (!inFile.eof() && read_number < total_blocks_number)
|
||||
{
|
||||
EASY_BLOCK("Read thread data", ::profiler::colors::Darkgreen);
|
||||
@ -333,7 +336,7 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic<int>& progr
|
||||
inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(blocks_number_in_thread)));
|
||||
|
||||
auto& root = threaded_trees[thread_id];
|
||||
const auto threshold = read_number + blocks_number_in_thread;
|
||||
auto threshold = read_number + blocks_number_in_thread;
|
||||
while (!inFile.eof() && read_number < threshold)
|
||||
{
|
||||
EASY_BLOCK("Read block", ::profiler::colors::Green);
|
||||
@ -350,8 +353,20 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic<int>& progr
|
||||
i += sz;
|
||||
auto baseData = reinterpret_cast<::profiler::SerializedBlock*>(data);
|
||||
|
||||
_blocks.emplace_back();
|
||||
::profiler::BlocksTree& tree = _blocks.back();
|
||||
if (cpu_frequency != 0)
|
||||
{
|
||||
auto t_begin = reinterpret_cast<::profiler::timestamp_t*>(data);
|
||||
auto t_end = t_begin + 1;
|
||||
|
||||
*t_begin *= 1000000000LL;
|
||||
*t_begin /= cpu_frequency;
|
||||
|
||||
*t_end *= 1000000000LL;
|
||||
*t_end /= cpu_frequency;
|
||||
}
|
||||
|
||||
blocks.emplace_back();
|
||||
::profiler::BlocksTree& tree = blocks.back();
|
||||
tree.node = baseData;// new ::profiler::SerializedBlock(sz, data);
|
||||
const auto block_index = blocks_counter++;
|
||||
|
||||
@ -388,7 +403,7 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic<int>& progr
|
||||
|
||||
if (!root.children.empty())
|
||||
{
|
||||
auto& back = _blocks[root.children.back()];
|
||||
auto& back = blocks[root.children.back()];
|
||||
auto t1 = back.node->end();
|
||||
auto mt0 = tree.node->begin();
|
||||
if (mt0 < t1)//parent - starts earlier than last ends
|
||||
@ -397,7 +412,7 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic<int>& progr
|
||||
/**/
|
||||
EASY_BLOCK("Find children", ::profiler::colors::Blue);
|
||||
auto rlower1 = ++root.children.rbegin();
|
||||
for (; rlower1 != root.children.rend() && !(mt0 > _blocks[*rlower1].node->begin()); ++rlower1);
|
||||
for (; rlower1 != root.children.rend() && !(mt0 > blocks[*rlower1].node->begin()); ++rlower1);
|
||||
auto lower = rlower1.base();
|
||||
::std::move(lower, root.children.end(), ::std::back_inserter(tree.children));
|
||||
|
||||
@ -416,7 +431,7 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic<int>& progr
|
||||
|
||||
for (auto i : tree.children)
|
||||
{
|
||||
auto& child = _blocks[i];
|
||||
auto& child = blocks[i];
|
||||
child.per_parent_stats = update_statistics(per_parent_statistics, child, i);
|
||||
|
||||
children_duration += child.node->duration();
|
||||
@ -428,7 +443,7 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic<int>& progr
|
||||
{
|
||||
for (auto i : tree.children)
|
||||
{
|
||||
const auto& child = _blocks[i];
|
||||
const auto& child = blocks[i];
|
||||
children_duration += child.node->duration();
|
||||
if (tree.depth < child.depth)
|
||||
tree.depth = child.depth;
|
||||
@ -453,12 +468,67 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic<int>& progr
|
||||
break;
|
||||
progress.store(10 + static_cast<int>(80 * i / memory_size));
|
||||
}
|
||||
|
||||
if (progress.load() < 0 || inFile.eof())
|
||||
break;
|
||||
|
||||
#ifdef EASY_STORE_CSWITCH_SEPARATELY
|
||||
blocks_number_in_thread = 0;
|
||||
inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(blocks_number_in_thread)));
|
||||
|
||||
threshold = read_number + blocks_number_in_thread;
|
||||
while (!inFile.eof() && read_number < threshold)
|
||||
{
|
||||
EASY_BLOCK("Read context switch", ::profiler::colors::Green);
|
||||
|
||||
++read_number;
|
||||
|
||||
uint16_t sz = 0;
|
||||
inFile.read((char*)&sz, sizeof(sz));
|
||||
if (sz == 0)
|
||||
return 0;
|
||||
|
||||
char* data = serialized_blocks[i];
|
||||
inFile.read(data, sz);
|
||||
i += sz;
|
||||
auto baseData = reinterpret_cast<::profiler::SerializedBlock*>(data);
|
||||
|
||||
if (cpu_frequency != 0)
|
||||
{
|
||||
auto t_begin = reinterpret_cast<::profiler::timestamp_t*>(data);
|
||||
auto t_end = t_begin + 1;
|
||||
|
||||
*t_begin *= 1000000000LL;
|
||||
*t_begin /= cpu_frequency;
|
||||
|
||||
*t_end *= 1000000000LL;
|
||||
*t_end /= cpu_frequency;
|
||||
}
|
||||
|
||||
blocks.emplace_back();
|
||||
::profiler::BlocksTree& tree = blocks.back();
|
||||
tree.node = baseData;
|
||||
const auto block_index = blocks_counter++;
|
||||
|
||||
auto descriptor = descriptors[baseData->id()];
|
||||
if (descriptor->type() != ::profiler::BLOCK_TYPE_CONTEXT_SWITCH)
|
||||
continue;
|
||||
|
||||
root.sync.emplace_back(block_index);
|
||||
|
||||
if (progress.load() < 0)
|
||||
break;
|
||||
|
||||
progress.store(10 + static_cast<int>(80 * i / memory_size));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (progress.load() < 0)
|
||||
{
|
||||
serialized_blocks.clear();
|
||||
threaded_trees.clear();
|
||||
blocks.clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -478,15 +548,22 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic<int>& progr
|
||||
auto& per_parent_statistics = parent_statistics[it.first];
|
||||
per_parent_statistics.clear();
|
||||
|
||||
statistics_threads.emplace_back(::std::thread([&per_parent_statistics, &per_frame_statistics, &_blocks](::profiler::BlocksTreeRoot& root)
|
||||
statistics_threads.emplace_back(::std::thread([&per_parent_statistics, &per_frame_statistics, &blocks](::profiler::BlocksTreeRoot& root)
|
||||
{
|
||||
#ifdef EASY_STORE_CSWITCH_SEPARATELY
|
||||
::std::sort(root.sync.begin(), root.sync.end(), [&blocks](::profiler::block_index_t left, ::profiler::block_index_t right)
|
||||
{
|
||||
return blocks[left].node->begin() < blocks[right].node->begin();
|
||||
});
|
||||
#endif
|
||||
|
||||
for (auto i : root.children)
|
||||
{
|
||||
auto& frame = _blocks[i];
|
||||
auto& frame = blocks[i];
|
||||
frame.per_parent_stats = update_statistics(per_parent_statistics, frame, i);
|
||||
|
||||
per_frame_statistics.clear();
|
||||
update_statistics_recursive(per_frame_statistics, frame, i, _blocks);
|
||||
update_statistics_recursive(per_frame_statistics, frame, i, blocks);
|
||||
|
||||
if (root.depth < frame.depth)
|
||||
root.depth = frame.depth;
|
||||
@ -511,10 +588,17 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic<int>& progr
|
||||
auto& root = it.second;
|
||||
root.thread_id = it.first;
|
||||
|
||||
#ifdef EASY_STORE_CSWITCH_SEPARATELY
|
||||
::std::sort(root.sync.begin(), root.sync.end(), [&blocks](::profiler::block_index_t left, ::profiler::block_index_t right)
|
||||
{
|
||||
return blocks[left].node->begin() < blocks[right].node->begin();
|
||||
});
|
||||
#endif
|
||||
|
||||
//root.tree.shrink_to_fit();
|
||||
for (auto i : root.children)
|
||||
{
|
||||
auto& frame = _blocks[i];
|
||||
auto& frame = blocks[i];
|
||||
if (root.depth < frame.depth)
|
||||
root.depth = frame.depth;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user