0
0
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:
Victor Zarubkin 2016-09-04 14:48:35 +03:00
parent 342a95108c
commit 05b56dcec0
12 changed files with 717 additions and 103 deletions

View 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_

View File

@ -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

View File

@ -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;

View File

@ -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
{

View File

@ -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

View File

@ -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

View File

@ -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
View 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
View 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_

View File

@ -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)
{
auto& t = thread_storage.second;
//outputFile << thread_storage.first << std::endl;
of.write(thread_storage.first);
of.write(static_cast<uint32_t>(thread_storage.second.closedList.size()));
#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 : thread_storage.second.closedList)
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;
}

View File

@ -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;
public:
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:
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

View File

@ -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;
}