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

(profiler_core) Further work with run-time enable/disable blocks;

(profiler_core) Fixed bug with memory corruption in chunk_allocator;
(profiler_core) Event tracing now gathering info about process which owns thread;
(profiler_core) Thread names are stored before other blocks now;
(profiler_core) Removed unused block types: context switch and thread sign;
(profiler_core) Added some description for API;
(profiler_gui) Context switch events now painted in different colors: red = switch to another process, coral = switch to registered thread in the same process, black = switch to kernel mode (thread id == 0).
(profiler_gui) Added popup windows with information about context-switch or block under mouse cursor;
This commit is contained in:
Victor Zarubkin 2016-09-13 23:03:01 +03:00
parent f490641165
commit 1b33ad788d
15 changed files with 1008 additions and 466 deletions

View File

@ -29,7 +29,7 @@ along with this program.If not, see <http://www.gnu.org/licenses/>.
#ifndef FULL_DISABLE_PROFILER
/**
\defgroup profiler Profiler
\defgroup profiler EasyProfiler
*/
/** If != 0 then EasyProfiler will measure time for blocks storage expansion.
@ -50,7 +50,16 @@ If false then you need to enable these events via GUI if you'll want to see them
*/
# define EASY_STORAGE_EXPAND_ENABLED true
/** Macro of beginning of block with custom name and color.
/** If true then EasyProfiler event tracing is enabled by default
and will be turned on and off when you call profiler::setEnabled().
Otherwise, it have to be turned on via GUI and then it will be
turned on/off with next calls of profiler::setEnabled().
\ingroup profiler
*/
# define EASY_EVENT_TRACING_ENABLED true
/** Macro for beginning of a block with custom name and color.
\code
#include "profiler/profiler.h"
@ -80,11 +89,11 @@ Block will be automatically completed by destructor.
*/
# define EASY_BLOCK(name, ...)\
static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__)(::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\
EASY_COMPILETIME_NAME(name), __FILE__, __LINE__, ::profiler::BLOCK_TYPE_BLOCK, ::profiler::extract_color(__VA_ARGS__)));\
EASY_UNIQUE_LINE_ID, EASY_COMPILETIME_NAME(name), __FILE__, __LINE__, ::profiler::BLOCK_TYPE_BLOCK, ::profiler::extract_color(__VA_ARGS__)));\
::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name));\
::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable
/** Macro of beginning of block with function name and custom color.
/** Macro for beginning of a block with function name and custom color.
\code
#include "profiler/profiler.h"
@ -110,11 +119,11 @@ Name of the block automatically created with function name.
*/
# define EASY_FUNCTION(...)\
static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__)(::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\
__func__, __FILE__, __LINE__, ::profiler::BLOCK_TYPE_BLOCK, ::profiler::extract_color(__VA_ARGS__)));\
EASY_UNIQUE_LINE_ID, __func__, __FILE__, __LINE__, ::profiler::BLOCK_TYPE_BLOCK, ::profiler::extract_color(__VA_ARGS__)));\
::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(EASY_UNIQUE_DESC(__LINE__), "");\
::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable
/** Macro of completion of last nearest open block.
/** Macro for completion of last opened block.
\code
#include "profiler/profiler.h"
@ -139,7 +148,7 @@ int foo()
*/
# define EASY_END_BLOCK ::profiler::endBlock();
/** Macro of creating event with custom name and color.
/** Macro for creating event with custom name and color.
Event is a block with zero duration and special type.
@ -150,44 +159,61 @@ will end previously opened EASY_BLOCK or EASY_FUNCTION.
*/
# define EASY_EVENT(name, ...)\
static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__)(\
::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__), EASY_COMPILETIME_NAME(name), __FILE__, __LINE__,\
::profiler::BLOCK_TYPE_EVENT, ::profiler::extract_color(__VA_ARGS__)));\
::profiler::storeBlock(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name));
::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__), EASY_UNIQUE_LINE_ID, EASY_COMPILETIME_NAME(name),\
__FILE__, __LINE__, ::profiler::BLOCK_TYPE_EVENT, ::profiler::extract_color(__VA_ARGS__)));\
::profiler::storeEvent(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name));
/** Macro enabling profiler
/** Macro for enabling profiler.
\ingroup profiler
*/
# define EASY_PROFILER_ENABLE ::profiler::setEnabled(true);
/** Macro disabling profiler
/** Macro for disabling profiler.
\ingroup profiler
*/
# define EASY_PROFILER_DISABLE ::profiler::setEnabled(false);
/** Macro of naming current thread.
/** Macro for current thread registration.
If this thread has been already named then nothing changes.
\note If this thread has been already registered then nothing happens.
\ingroup profiler
*/
# define EASY_THREAD(name)\
EASY_THREAD_LOCAL static const char* EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = nullptr;\
if (EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) == nullptr)\
EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = ::profiler::setThreadName(name, __FILE__, __func__, __LINE__);
EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = ::profiler::registerThread(name);
/** Macro of naming main thread.
/** Macro for main thread registration.
This is only for user comfort. There is no difference for EasyProfiler GUI between different threads.
This is just for user's comfort. There is no difference for EasyProfiler GUI between different threads.
\ingroup profiler
*/
# define EASY_MAIN_THREAD EASY_THREAD("Main")
# ifndef _WIN32
/** Macro for setting temporary log-file path for Unix event tracing system.
\note Default value is "/tmp/cs_profiling_info.log".
\ingroup profiler
*/
# define EASY_EVENT_TRACING_SET_LOG(filename) ::profiler::setContextSwitchLogFilename(filename);
/** Macro returning current path to the temporary log-file for Unix event tracing system.
\ingroup profiler
*/
# define EASY_EVENT_TRACING_LOG ::profiler::getContextSwitchLogFilename();
# endif
#else
# define EASY_MEASURE_STORAGE_EXPAND 0
# define EASY_STORAGE_EXPAND_ENABLED false
# define EASY_EVENT_TRACING_ENABLED false
# define EASY_BLOCK(...)
# define EASY_FUNCTION(...)
# define EASY_END_BLOCK
@ -196,6 +222,12 @@ This is only for user comfort. There is no difference for EasyProfiler GUI betwe
# define EASY_EVENT(...)
# define EASY_THREAD(...)
# define EASY_MAIN_THREAD
# ifndef _WIN32
# define EASY_EVENT_TRACING_SET_LOG(filename)
# define EASY_EVENT_TRACING_LOG ""
# endif
#endif
//////////////////////////////////////////////////////////////////////////
@ -216,9 +248,7 @@ namespace profiler {
enum BlockType : uint8_t
{
BLOCK_TYPE_EVENT = 0,
BLOCK_TYPE_THREAD_SIGN,
BLOCK_TYPE_BLOCK,
BLOCK_TYPE_CONTEXT_SWITCH,
BLOCK_TYPES_NUMBER
};
@ -290,9 +320,10 @@ namespace profiler {
const char* m_name; ///< Static name of all blocks of the same type (blocks can have dynamic name) which is, in pair with descriptor id, a unique block identifier
const char* m_filename; ///< Source file name where this block is declared
bool* m_pEnable; ///< Pointer to the enable flag in unordered_map
bool m_expired; ///< Is this descriptor expired
BlockDescriptor(uint64_t& _used_mem, bool _enabled, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color);
void setId(block_id_t _id);
public:
@ -336,6 +367,11 @@ namespace profiler {
inline const char* name() const { return m_name; }
private:
Block(const Block&) = delete;
Block& operator = (const Block&) = delete;
}; // END of class Block.
//***********************************************
@ -360,17 +396,80 @@ namespace profiler {
//////////////////////////////////////////////////////////////////////
// Core API
// Note: it is better to use macros defined above than a direct calls to API.
extern "C" {
PROFILER_API const BaseBlockDescriptor& registerDescription(bool _enabled, const char* _compiletimeName, const char* _filename, int _line, block_type_t _block_type, color_t _color);
PROFILER_API void storeBlock(const BaseBlockDescriptor& _desc, const char* _runtimeName);
PROFILER_API void beginBlock(Block& _block);
PROFILER_API void endBlock();
PROFILER_API void setEnabled(bool _isEnable);
PROFILER_API uint32_t dumpBlocksToFile(const char* _filename);
PROFILER_API const char* setThreadName(const char* _name, const char* _filename, const char* _funcname, int _line);
PROFILER_API void setContextSwitchLogFilename(const char* _name);
/** Registers static description of a block.
It is general information which is common for all such blocks.
Includes color, block type (see BlockType), file-name, line-number, compile-time name of a block and enable-flag.
\ingroup profiler
*/
PROFILER_API const BaseBlockDescriptor& registerDescription(bool _enabled, const char* _autogenUniqueId, const char* _compiletimeName, const char* _filename, int _line, block_type_t _block_type, color_t _color);
/** Stores event in the blocks list.
An event ends instantly and has zero duration.
\param _desc Reference to the previously registered description.
\param _runtimeName Standard zero-terminated string which will be copied to the events buffer.
\ingroup profiler
*/
PROFILER_API void storeEvent(const BaseBlockDescriptor& _desc, const char* _runtimeName);
/** Begins block.
\ingroup profiler
*/
PROFILER_API void beginBlock(Block& _block);
/** Ends last started block.
\ingroup profiler
*/
PROFILER_API void endBlock();
/** Enable or disable profiler.
\ingroup profiler
*/
PROFILER_API void setEnabled(bool _isEnable);
/** Save all gathered blocks into file.
\note This also disables profiler.
\ingroup profiler
*/
PROFILER_API uint32_t dumpBlocksToFile(const char* _filename);
/** Register current thread and give it a name.
\note Only first call of registerThread() for the current thread will have an effect.
\ingroup profiler
*/
PROFILER_API const char* registerThread(const char* _name);
#ifndef _WIN32
/** Set temporary log-file path for Unix event tracing system.
\note Default value is "/tmp/cs_profiling_info.log".
\ingroup profiler
*/
PROFILER_API void setContextSwitchLogFilename(const char* _name);
/** Returns current path to the temporary log-file for Unix event tracing system.
\ingroup profiler
*/
PROFILER_API const char* getContextSwitchLogFilename();
#endif
}
inline void setEnabled(::profiler::EasyEnableFlag _isEnable) {

View File

@ -86,6 +86,12 @@ namespace profiler {
ENABLED = 1
};
struct passthrough_hash final {
template <class T> inline size_t operator () (T _value) const {
return static_cast<size_t>(_value);
}
};
}
//////////////////////////////////////////////////////////////////////////
@ -95,6 +101,7 @@ namespace profiler {
#include <type_traits>
# define EASY_STRINGIFY(a) #a
# define EASY_STRINGIFICATION(a) EASY_STRINGIFY(a)
# define EASY_TOKEN_JOIN(x, y) x ## y
# define EASY_TOKEN_CONCATENATE(x, y) EASY_TOKEN_JOIN(x, y)
# define EASY_UNIQUE_BLOCK(x) EASY_TOKEN_CONCATENATE(unique_profiler_mark_name_, x)
@ -165,7 +172,7 @@ namespace profiler {
} // END of namespace profiler.
# define EASY_UNIQUE_LINE_ID __FILE__ ":" EASY_STRINGIFY(__LINE__)
# define EASY_UNIQUE_LINE_ID __FILE__ ":" EASY_STRINGIFICATION(__LINE__)
# define EASY_COMPILETIME_NAME(name) ::profiler::NameSwitch<::std::is_reference<decltype(name)>::value>::compiletime_name(name, EASY_UNIQUE_LINE_ID)
# define EASY_RUNTIME_NAME(name) ::profiler::NameSwitch<::std::is_reference<decltype(name)>::value>::runtime_name(name)

View File

@ -21,8 +21,9 @@ along with this program.If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <map>
#include <unordered_map>
#include <vector>
#include <string>
#include <atomic>
#include "profiler/profiler.h"
#include "profiler/serialized_block.h"
@ -175,7 +176,7 @@ namespace profiler {
BlocksTree::children_t children;
BlocksTree::children_t sync;
const char* thread_name;
std::string thread_name;
::profiler::thread_id_t thread_id;
uint16_t depth;
@ -186,7 +187,7 @@ namespace profiler {
BlocksTreeRoot(This&& that)
: children(::std::move(that.children))
, sync(::std::move(that.sync))
, thread_name(that.thread_name)
, thread_name(::std::move(that.thread_name))
, thread_id(that.thread_id)
, depth(that.depth)
{
@ -196,12 +197,24 @@ namespace profiler {
{
children = ::std::move(that.children);
sync = ::std::move(that.sync);
thread_name = that.thread_name;
thread_name = ::std::move(that.thread_name);
thread_id = that.thread_id;
depth = that.depth;
return *this;
}
inline bool gotName() const
{
//return thread_name && *thread_name != 0;
return thread_name.front() != 0;
}
inline const char* name() const
{
//return thread_name;
return thread_name.c_str();
}
bool operator < (const This& other) const
{
return thread_id < other.thread_id;
@ -215,7 +228,7 @@ namespace profiler {
}; // END of class BlocksTreeRoot.
typedef ::profiler::BlocksTree::blocks_t blocks_t;
typedef ::std::map<::profiler::thread_id_t, ::profiler::BlocksTreeRoot> thread_blocks_tree_t;
typedef ::std::unordered_map<::profiler::thread_id_t, ::profiler::BlocksTreeRoot, ::profiler::passthrough_hash> thread_blocks_tree_t;
//////////////////////////////////////////////////////////////////////////

File diff suppressed because it is too large Load Diff

View File

@ -161,6 +161,7 @@ public:
void getBlocks(qreal _left, qreal _right, ::profiler_gui::TreeBlocks& _blocks) const;
const ::profiler_gui::EasyBlockItem* intersect(const QPointF& _pos) const;
const ::profiler_gui::EasyBlock* intersectEvent(const QPointF& _pos) const;
private:
@ -272,6 +273,8 @@ EASY_QGRAPHICSITEM(EasyTimelineIndicatorItem);
//////////////////////////////////////////////////////////////////////////
class QGraphicsProxyWidget;
class EasyGraphicsView : public QGraphicsView
{
Q_OBJECT
@ -287,19 +290,24 @@ private:
Keys m_keys; ///< Pressed keys
::profiler_gui::TreeBlocks m_selectedBlocks; ///< Array of items which were selected by selection zone (EasyChronometerItem)
QTimer m_flickerTimer; ///< Timer for flicking behavior
QTimer m_idleTimer; ///<
QRectF m_visibleSceneRect; ///< Visible scene rectangle
::profiler::timestamp_t m_beginTime; ///< Begin time of profiler session. Used to reduce values of all begin and end times of profiler blocks.
qreal m_scale; ///< Current scale
qreal m_offset; ///< Have to use manual offset for all scene content instead of using scrollbars because QScrollBar::value is 32-bit integer :(
qreal m_timelineStep; ///<
uint64_t m_idleTime; ///<
QPoint m_mousePressPos; ///< Last mouse global position (used by mousePressEvent and mouseMoveEvent)
QPoint m_mouseMovePath; ///< Mouse move path between press and release of any button
Qt::MouseButtons m_mouseButtons; ///< Pressed mouse buttons
EasyGraphicsScrollbar* m_pScrollbar; ///< Pointer to the graphics scrollbar widget
EasyChronometerItem* m_chronometerItem; ///< Pointer to the EasyChronometerItem which is displayed when you press right mouse button and move mouse left or right. This item is used to select blocks to display in tree widget.
EasyChronometerItem* m_chronometerItemAux; ///< Pointer to the EasyChronometerItem which is displayed when you double click left mouse button and move mouse left or right. This item is used only to measure time.
QGraphicsProxyWidget* m_csInfoWidget; ///<
int m_flickerSpeedX; ///< Current flicking speed x
int m_flickerSpeedY; ///< Current flicking speed y
int m_flickerCounterX;
int m_flickerCounterY;
bool m_bDoubleClick; ///< Is mouse buttons double clicked
bool m_bUpdatingRect; ///< Stub flag which is used to avoid excess calculations on some scene update (flicking, scaling and so on)
bool m_bEmpty; ///< Indicates whether scene is empty and has no items
@ -361,6 +369,7 @@ private slots:
void onScrollbarValueChange(int);
void onGraphicsScrollbarValueChange(qreal);
void onFlickerTimeout();
void onIdleTimeout();
void onSelectedThreadChange(::profiler::thread_id_t _id);
void onSelectedBlockChange(unsigned int _block_index);
void onItemsEspandStateChange();

View File

@ -138,7 +138,6 @@ struct EasyBlockItem final
//const ::profiler::BlocksTree* block; ///< Pointer to profiler block
qreal x; ///< x coordinate of the item (this is made qreal=double to avoid mistakes on very wide scene)
float w; ///< Width of the item
QRgb color; ///< Background color of the item
::profiler::block_index_t block; ///< Index of profiler block
uint32_t children_begin; ///< Index of first child item on the next sublevel
uint16_t totalHeight; ///< Total height of the item including heights of all it's children

View File

@ -515,9 +515,9 @@ void EasyGraphicsScrollbar::contextMenuEvent(QContextMenuEvent* _event)
for (const auto& it : EASY_GLOBALS.profiler_blocks)
{
QString label;
if (it.second.thread_name && it.second.thread_name[0] != 0)
if (it.second.gotName())
{
label = ::std::move(QString("%1 Thread %2").arg(it.second.thread_name).arg(it.first));
label = ::std::move(QString("%1 Thread %2").arg(it.second.name()).arg(it.first));
}
else
{

View File

@ -161,20 +161,14 @@ void FillTreeClass<T>::setTreeInternal1(T& _safelocker, Items& _items, ThreadedI
const auto& root = threadTree.second;
auto item = new EasyTreeWidgetItem();
if (root.thread_name && root.thread_name[0] != 0)
{
item->setText(COL_NAME, QString("%1 Thread %2").arg(root.thread_name).arg(root.thread_id));
}
if (root.gotName())
item->setText(COL_NAME, QString("%1 Thread %2").arg(root.name()).arg(root.thread_id));
else
{
item->setText(COL_NAME, QString("Thread %1").arg(root.thread_id));
}
::profiler::timestamp_t duration = 0;
if (!root.children.empty())
{
duration = blocksTree(root.children.back()).node->end() - blocksTree(root.children.front()).node->begin();
}
item->setTimeSmart(COL_DURATION, duration);
item->setBackgroundColor(::profiler_gui::SELECTED_THREAD_BACKGROUND);
@ -259,19 +253,13 @@ void FillTreeClass<T>::setTreeInternal2(T& _safelocker, Items& _items, ThreadedI
{
thread_item = new EasyTreeWidgetItem();
if (block.root->thread_name && block.root->thread_name[0] != 0)
{
thread_item->setText(COL_NAME, QString("%1 Thread %2").arg(block.root->thread_name).arg(block.root->thread_id));
}
if (block.root->gotName())
thread_item->setText(COL_NAME, QString("%1 Thread %2").arg(block.root->name()).arg(block.root->thread_id));
else
{
thread_item->setText(COL_NAME, QString("Thread %1").arg(block.root->thread_id));
}
if (!block.root->children.empty())
{
duration = blocksTree(block.root->children.back()).node->end() - blocksTree(block.root->children.front()).node->begin();
}
thread_item->setTimeSmart(COL_DURATION, duration);
thread_item->setBackgroundColor(::profiler_gui::SELECTED_THREAD_BACKGROUND);

View File

@ -3,7 +3,9 @@
#ifdef _WIN32
#include <memory.h>
#include <chrono>
#include <unordered_map>
#include "event_trace_win.h"
#include "Psapi.h"
#include "profiler/profiler.h"
#include "profile_manager.h"
@ -15,6 +17,16 @@
namespace profiler {
//////////////////////////////////////////////////////////////////////////
struct ProcessInfo final {
std::string name;
uint32_t id = 0;
int8_t valid = 0;
};
//////////////////////////////////////////////////////////////////////////
// CSwitch class
// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa964744(v=vs.85).aspx
// EventType = 36
@ -34,6 +46,17 @@ namespace profiler {
uint32_t Reserved;
};
//////////////////////////////////////////////////////////////////////////
typedef ::std::unordered_map<decltype(CSwitch::NewThreadId), ProcessInfo*, ::profiler::do_not_calc_hash> thread_process_info_map;
typedef ::std::unordered_map<uint32_t, ProcessInfo, ::profiler::do_not_calc_hash> process_info_map;
// Using static is safe because processTraceEvent() is called from one thread
static process_info_map PROCESS_INFO_TABLE;
static thread_process_info_map THREAD_PROCESS_INFO_TABLE = ([](){ thread_process_info_map initial; initial[0U] = nullptr; return ::std::move(initial); })();
//////////////////////////////////////////////////////////////////////////
void WINAPI processTraceEvent(PEVENT_RECORD _traceEvent)
{
static const decltype(_traceEvent->EventHeader.EventDescriptor.Opcode) SWITCH_CONTEXT_OPCODE = 36;
@ -43,13 +66,79 @@ namespace profiler {
if (sizeof(CSwitch) != _traceEvent->UserDataLength)
return;
//EASY_FUNCTION(::profiler::colors::White);
EASY_FUNCTION(::profiler::colors::White, ::profiler::DISABLED);
auto _contextSwitchEvent = reinterpret_cast<CSwitch*>(_traceEvent->UserData);
const auto time = static_cast<::profiler::timestamp_t>(_traceEvent->EventHeader.TimeStamp.QuadPart);
static const auto& desc = MANAGER.addBlockDescriptor(true, "OS.ContextSwitch", __FILE__, __LINE__, ::profiler::BLOCK_TYPE_CONTEXT_SWITCH, ::profiler::colors::White);
MANAGER.beginContextSwitch(_contextSwitchEvent->OldThreadId, time, desc.id());
ProcessInfo* pinfo = nullptr;
auto it = THREAD_PROCESS_INFO_TABLE.find(_contextSwitchEvent->NewThreadId);
if (it == THREAD_PROCESS_INFO_TABLE.end())
{
auto hThread = OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, _contextSwitchEvent->NewThreadId);
if (hThread != nullptr)
{
auto pid = GetProcessIdOfThread(hThread);
pinfo = &PROCESS_INFO_TABLE[pid];
if (pinfo->valid == 0)
{
// According to documentation, using GetModuleBaseName() requires
// PROCESS_QUERY_INFORMATION | PROCESS_VM_READ access rights.
// But it works fine with PROCESS_QUERY_LIMITED_INFORMATION instead of PROCESS_QUERY_INFORMATION.
//
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683196(v=vs.85).aspx
auto hProc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, FALSE, pid);
if (hProc != nullptr)
{
static TCHAR buf[MAX_PATH] = {}; // Using static is safe because processTraceEvent() is called from one thread
auto success = GetModuleBaseName(hProc, 0, buf, MAX_PATH);
if (success)
{
static char numbuf[128] = {};
sprintf(numbuf, "%u ", pid);
pinfo->name = numbuf;
pinfo->name += buf;
pinfo->id = pid;
pinfo->valid = 1;
//printf("PROCESS %u is %s\n", pid, buf);
}
CloseHandle(hProc);
}
else
{
pinfo->valid = -1;
}
}
if (pinfo->valid > 0)
{
THREAD_PROCESS_INFO_TABLE[_contextSwitchEvent->NewThreadId] = pinfo;
}
else if (pinfo->valid < 0)
{
pinfo = nullptr;
THREAD_PROCESS_INFO_TABLE[_contextSwitchEvent->NewThreadId] = nullptr;
}
CloseHandle(hThread);
}
else
{
THREAD_PROCESS_INFO_TABLE[_contextSwitchEvent->NewThreadId] = nullptr;
}
}
else
{
pinfo = it->second;
if (pinfo != nullptr && pinfo->valid < 0)
pinfo = nullptr;
}
MANAGER.beginContextSwitch(_contextSwitchEvent->OldThreadId, time, _contextSwitchEvent->NewThreadId, pinfo ? pinfo->name.c_str() : "");
MANAGER.endContextSwitch(_contextSwitchEvent->NewThreadId, time);
}
@ -109,6 +198,7 @@ namespace profiler {
{
static const decltype(m_properties.base.Wnode.ClientContext) RAW_TIMESTAMP_TIME_TYPE = 1;
profiler::guard_lock<profiler::spin_lock> lock(m_spin);
if (m_bEnabled)
return EVENT_TRACING_LAUNCHED_SUCCESSFULLY;
@ -142,12 +232,15 @@ namespace profiler {
// 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]()
m_processThread = ::std::move(::std::thread([this]()
{
EASY_THREAD("EasyProfiler.ETW");
ProcessTrace(&m_openedHandle, 1, 0, 0);
}));
// Set low priority for event tracing thread
SetThreadPriority(m_processThread.native_handle(), THREAD_PRIORITY_LOWEST);
m_bEnabled = true;
return EVENT_TRACING_LAUNCHED_SUCCESSFULLY;
@ -155,17 +248,23 @@ namespace profiler {
void EasyEventTracer::disable()
{
profiler::guard_lock<profiler::spin_lock> lock(m_spin);
if (!m_bEnabled)
return;
ControlTrace(m_openedHandle, KERNEL_LOGGER_NAME, props(), EVENT_TRACE_CONTROL_STOP);
CloseTrace(m_openedHandle);
// Wait for ProcessThread to finish
if (m_stubThread.joinable())
m_stubThread.join();
// Wait for ProcessTrace to finish to make sure no processTraceEvent() will be called later.
if (m_processThread.joinable())
m_processThread.join();
m_bEnabled = false;
// processTraceEvent() is not called anymore. Clean static maps is safe.
PROCESS_INFO_TABLE.clear();
THREAD_PROCESS_INFO_TABLE.clear();
THREAD_PROCESS_INFO_TABLE[0U] = nullptr;
}
} // END of namespace profiler.

View File

@ -12,7 +12,8 @@
#include <evntrace.h>
#include <evntcons.h>
#include <thread>
#include "profiler/event_trace_status.h"
#include "event_trace_status.h"
#include "spin_lock.h"
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
@ -29,12 +30,13 @@ namespace profiler {
};
#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;
::std::thread m_processThread;
Properties m_properties;
EVENT_TRACE_LOGFILE m_trace;
profiler::spin_lock m_spin;
TRACEHANDLE m_sessionHandle = INVALID_PROCESSTRACE_HANDLE;
TRACEHANDLE m_openedHandle = INVALID_PROCESSTRACE_HANDLE;
bool m_bEnabled = false;
public:

View File

@ -64,7 +64,11 @@ namespace profiler {
public:
explicit cstring(const char* _str) : m_str(_str), m_len(strlen(_str))
cstring(const char* _str) : m_str(_str), m_len(strlen(_str))
{
}
cstring(const char* _str, size_t _len) : m_str(_str), m_len(_len)
{
}
@ -91,6 +95,16 @@ namespace profiler {
return m_len < _other.m_len;
}
inline const char* c_str() const
{
return m_str;
}
inline size_t size() const
{
return m_len;
}
}; // END of class cstring.
/** \brief cstring with precalculated hash.
@ -110,11 +124,19 @@ namespace profiler {
public:
explicit hashed_cstr(const char* _str) : Parent(_str), m_hash(0)
hashed_cstr(const char* _str) : Parent(_str), m_hash(0)
{
m_hash = ::std::_Hash_seq((const unsigned char *)m_str, m_len);
}
hashed_cstr(const char* _str, size_t _hash_code) : Parent(_str), m_hash(_hash_code)
{
}
hashed_cstr(const char* _str, size_t _len, size_t _hash_code) : Parent(_str, _len), m_hash(_hash_code)
{
}
hashed_cstr(const hashed_cstr&) = default;
hashed_cstr& operator = (const hashed_cstr&) = default;
@ -206,6 +228,16 @@ namespace profiler {
return m_hash;
}
inline const char* c_str() const
{
return m_str.c_str();
}
inline size_t size() const
{
return m_str.size();
}
}; // END of class hashed_stdstring.
} // END of namespace profiler.

View File

@ -47,9 +47,9 @@ extern timestamp_t getCurrentTime();
extern "C" {
PROFILER_API const BaseBlockDescriptor& registerDescription(bool _enabled, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color)
PROFILER_API const BaseBlockDescriptor& registerDescription(bool _enabled, const char* _autogenUniqueId, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color)
{
return MANAGER.addBlockDescriptor(_enabled, _name, _filename, _line, _block_type, _color);
return MANAGER.addBlockDescriptor(_enabled, _autogenUniqueId, _name, _filename, _line, _block_type, _color);
}
PROFILER_API void endBlock()
@ -60,15 +60,9 @@ extern "C" {
PROFILER_API void setEnabled(bool isEnable)
{
MANAGER.setEnabled(isEnable);
#ifdef _WIN32
if (isEnable)
EasyEventTracer::instance().enable(true);
else
EasyEventTracer::instance().disable();
#endif
}
PROFILER_API void storeBlock(const BaseBlockDescriptor& _desc, const char* _runtimeName)
PROFILER_API void storeEvent(const BaseBlockDescriptor& _desc, const char* _runtimeName)
{
MANAGER.storeBlock(_desc, _runtimeName);
}
@ -83,11 +77,12 @@ extern "C" {
return MANAGER.dumpBlocksToFile(filename);
}
PROFILER_API const char* setThreadName(const char* name, const char* filename, const char* _funcname, int line)
PROFILER_API const char* registerThread(const char* name)//, const char* filename, const char* _funcname, int line)
{
return MANAGER.setThreadName(name, filename, _funcname, line);
return MANAGER.registerThread(name);// , filename, _funcname, line);
}
#ifndef _WIN32
PROFILER_API void setContextSwitchLogFilename(const char* name)
{
return MANAGER.setContextSwitchLogFilename(name);
@ -97,6 +92,7 @@ extern "C" {
{
return MANAGER.getContextSwitchLogFilename();
}
#endif
}
@ -124,6 +120,8 @@ BlockDescriptor::BlockDescriptor(uint64_t& _used_mem, block_id_t _id, bool _enab
: BaseBlockDescriptor(_id, _enabled, _line, _block_type, _color)
, m_name(_name)
, m_filename(_filename)
, m_pEnable(nullptr)
, m_expired(false)
{
_used_mem += sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2;
}
@ -132,13 +130,10 @@ BlockDescriptor::BlockDescriptor(uint64_t& _used_mem, bool _enabled, const char*
: BaseBlockDescriptor(0, _enabled, _line, _block_type, _color)
, m_name(_name)
, m_filename(_filename)
, m_pEnable(nullptr)
, m_expired(false)
{
_used_mem += sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2;
}
void BlockDescriptor::setId(block_id_t _id)
{
m_id = _id;
}
BlockDescRef::~BlockDescRef()
@ -151,7 +146,7 @@ BlockDescRef::~BlockDescRef()
void ThreadStorage::storeBlock(const profiler::Block& block)
{
#if EASY_MEASURE_STORAGE_EXPAND != 0
static const auto desc = MANAGER.addBlockDescriptor(EASY_STORAGE_EXPAND_ENABLED, "EasyProfiler.ExpandStorage", __FILE__, __LINE__, profiler::BLOCK_TYPE_BLOCK, profiler::colors::White);
static const auto desc = MANAGER.addBlockDescriptor(EASY_STORAGE_EXPAND_ENABLED, EASY_UNIQUE_LINE_ID, "EasyProfiler.ExpandStorage", __FILE__, __LINE__, profiler::BLOCK_TYPE_BLOCK, profiler::colors::White);
#endif
auto name_length = static_cast<uint16_t>(strlen(block.name()));
@ -205,7 +200,8 @@ EASY_THREAD_LOCAL static ::ThreadStorage* THREAD_STORAGE = nullptr;
ProfileManager::ProfileManager()
{
m_expiredDescriptors.reserve(1024U);
m_isEnabled = ATOMIC_VAR_INIT(false);
m_isEventTracingEnabled = ATOMIC_VAR_INIT(EASY_EVENT_TRACING_ENABLED);
}
ProfileManager::~ProfileManager()
@ -213,7 +209,8 @@ ProfileManager::~ProfileManager()
//dumpBlocksToFile("test.prof");
for (auto desc : m_descriptors)
{
delete desc;
if (desc != nullptr)
delete desc;
}
}
@ -231,7 +228,7 @@ void ProfileManager::markExpired(profiler::block_id_t _id)
// We can not delete this descriptor now, because we need to send/write all collected data first.
guard_lock_t lock(m_storedSpin);
m_expiredDescriptors.push_back(_id);
m_descriptors[_id]->m_expired = true;
}
ThreadStorage& ProfileManager::threadStorage(profiler::thread_id_t _thread_id)
@ -240,9 +237,8 @@ ThreadStorage& ProfileManager::threadStorage(profiler::thread_id_t _thread_id)
return m_threads[_thread_id];
}
ThreadStorage* ProfileManager::findThreadStorage(profiler::thread_id_t _thread_id)
ThreadStorage* ProfileManager::_findThreadStorage(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;
}
@ -272,19 +268,21 @@ void ProfileManager::beginBlock(Block& _block)
THREAD_STORAGE->blocks.openedList.emplace(_block);
}
void ProfileManager::beginContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id)
void ProfileManager::beginContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::thread_id_t _target_thread_id, const char* _target_process, bool _lockSpin)
{
auto ts = findThreadStorage(_thread_id);
auto ts = _lockSpin ? findThreadStorage(_thread_id) : _findThreadStorage(_thread_id);
if (ts != nullptr)
ts->sync.openedList.emplace(_time, _id, "");
// Dirty hack: _target_thread_id will be written to the field "block_id_t m_id"
// and will be available calling method id().
ts->sync.openedList.emplace(_time, _target_thread_id, _target_process);
}
void ProfileManager::storeContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id)
void ProfileManager::storeContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::thread_id_t _target_thread_id, bool _lockSpin)
{
auto ts = findThreadStorage(_thread_id);
auto ts = _lockSpin ? findThreadStorage(_thread_id) : _findThreadStorage(_thread_id);
if (ts != nullptr)
{
profiler::Block b(_time, _id, "");
profiler::Block b(_time, _target_thread_id, "");
b.finish(_time);
ts->storeCSwitch(b);
}
@ -309,9 +307,9 @@ void ProfileManager::endBlock()
THREAD_STORAGE->blocks.openedList.pop();
}
void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime)
void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime, bool _lockSpin)
{
auto ts = findThreadStorage(_thread_id);
auto ts = _lockSpin ? findThreadStorage(_thread_id) : _findThreadStorage(_thread_id);
if (ts == nullptr || ts->sync.openedList.empty())
return;
@ -325,6 +323,15 @@ void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, profiler
void ProfileManager::setEnabled(bool isEnable)
{
m_isEnabled = isEnable;
#ifdef _WIN32
if (isEnable) {
if (m_isEventTracingEnabled)
EasyEventTracer::instance().enable(true);
} else {
EasyEventTracer::instance().disable();
}
#endif
}
//////////////////////////////////////////////////////////////////////////
@ -332,29 +339,37 @@ void ProfileManager::setEnabled(bool isEnable)
uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
{
const bool wasEnabled = m_isEnabled;
const bool eventTracingEnabled = m_isEventTracingEnabled;
if (wasEnabled)
::profiler::setEnabled(false);
// This is to make sure that no new descriptors or new threads will be
// added until we finish sending data.
guard_lock_t lock1(m_storedSpin);
guard_lock_t lock2(m_spin);
// This is the only place using both spins, so no dead-lock will occur
#ifndef _WIN32
// Read thread context switch events from temporary file
uint64_t timestamp;
uint32_t thread_from, thread_to;
std::ifstream infile(m_csInfoFilename.c_str());
if(infile.is_open())
if (eventTracingEnabled)
{
static const auto& desc = addBlockDescriptor(true, "OS.ContextSwitch", __FILE__, __LINE__, profiler::BLOCK_TYPE_CONTEXT_SWITCH, profiler::colors::White);
while (infile >> timestamp >> thread_from >> thread_to)
{
beginContextSwitch(thread_from, timestamp, desc.id());
endContextSwitch(thread_to, timestamp);
// Read thread context switch events from temporary file
uint64_t timestamp = 0;
uint32_t thread_from = 0, thread_to = 0;
std::ifstream infile(m_csInfoFilename.c_str());
if(infile.is_open()) {
while (infile >> timestamp >> thread_from >> thread_to) {
beginContextSwitch(thread_from, timestamp, thread_to, "", false);
endContextSwitch(thread_to, timestamp, false);
}
}
}
#endif
// Calculate used memory total size and total blocks number
uint64_t usedMemorySize = 0;
uint32_t blocks_number = 0;
@ -381,6 +396,9 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
// Write block descriptors
for (const auto descriptor : m_descriptors)
{
if (descriptor == nullptr)
continue;
const auto name_size = static_cast<uint16_t>(strlen(descriptor->name()) + 1);
const auto filename_size = static_cast<uint16_t>(strlen(descriptor->file()) + 1);
const auto size = static_cast<uint16_t>(sizeof(profiler::SerializedBlockDescriptor) + name_size + filename_size);
@ -399,27 +417,30 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
_outputStream.write(thread_storage.first);
const auto name_size = static_cast<uint16_t>(t.name.size() + 1);
_outputStream.write(name_size);
_outputStream.write(name_size > 1 ? t.name.c_str() : "", name_size);
_outputStream.write(t.sync.closedList.size());
t.sync.closedList.serialize(_outputStream);
_outputStream.write(t.blocks.closedList.size());
t.blocks.closedList.serialize(_outputStream);
if (!t.blocks.closedList.empty())
t.blocks.closedList.serialize(_outputStream);
t.clearClosed();
t.blocks.openedList.clear();
t.sync.openedList.clear();
}
if (!m_expiredDescriptors.empty())
// Remove all expired block descriptors (descriptor may become expired if it's .dll/.so have been unloaded during application execution)
for (auto& desc : m_descriptors)
{
// Remove all expired block descriptors (descriptor may become expired if it's .dll/.so have been unloaded during application execution)
if (desc == nullptr || !desc->m_expired)
continue;
std::sort(m_expiredDescriptors.begin(), m_expiredDescriptors.end());
for (auto it = m_expiredDescriptors.rbegin(), rend = m_expiredDescriptors.rend(); it != rend; ++it)
{
auto id = *it;
delete m_descriptors[id];
m_descriptors.erase(m_descriptors.begin() + id);
}
m_expiredDescriptors.clear();
delete desc;
desc = nullptr;
}
//if (wasEnabled)
@ -439,7 +460,7 @@ uint32_t ProfileManager::dumpBlocksToFile(const char* _filename)
return blocksNumber;
}
const char* ProfileManager::setThreadName(const char* name, const char* filename, const char* _funcname, int line)
const char* ProfileManager::registerThread(const char* name)
{
if (THREAD_STORAGE == nullptr)
{
@ -448,18 +469,31 @@ const char* ProfileManager::setThreadName(const char* name, const char* filename
if (!THREAD_STORAGE->named)
{
const auto& desc = addBlockDescriptor(true, _funcname, filename, line, profiler::BLOCK_TYPE_THREAD_SIGN, profiler::colors::Black);
profiler::Block b(desc, name);
b.finish(b.begin());
THREAD_STORAGE->storeBlock(b);
THREAD_STORAGE->name = name;
THREAD_STORAGE->named = true;
THREAD_STORAGE->name = name;
}
return THREAD_STORAGE->name.c_str();
}
void ProfileManager::setBlockEnabled(profiler::block_id_t _id, const profiler::hashed_stdstring& _key, bool _enabled)
{
guard_lock_t lock(m_storedSpin);
auto desc = m_descriptors[_id];
if (desc != nullptr)
{
lock.unlock();
*desc->m_pEnable = _enabled;
desc->m_enabled = _enabled;
}
else
{
blocks_enable_status_t::key_type key(_key.c_str(), _key.size(), _key.hcode());
m_blocksEnableStatus[key] = _enabled;
}
}
//////////////////////////////////////////////////////////////////////////

View File

@ -22,12 +22,14 @@ along with this program.If not, see <http://www.gnu.org/licenses/>.
#include "profiler/profiler.h"
#include "spin_lock.h"
#include "outstream.h"
//#include "hashed_cstr.h"
#include "hashed_cstr.h"
#include <map>
#include <list>
#include <vector>
#include <unordered_map>
#include <string>
#include <functional>
#include <atomic>
//////////////////////////////////////////////////////////////////////////
@ -51,7 +53,17 @@ inline uint32_t getCurrentThreadId()
#endif
}
namespace profiler { class SerializedBlock; }
namespace profiler {
class SerializedBlock;
struct do_not_calc_hash {
template <class T> inline size_t operator()(T _value) const {
return static_cast<size_t>(_value);
}
};
}
//////////////////////////////////////////////////////////////////////////
@ -149,7 +161,7 @@ public:
*(uint16_t*)data = n;
data = data + sizeof(uint16_t);
if (m_shift < N)
if (m_shift + 1 < N)
*(uint16_t*)(data + n) = 0;
return data;
@ -176,6 +188,11 @@ public:
return m_size;
}
inline bool empty() const
{
return m_size == 0;
}
void clear()
{
m_size = 0;
@ -201,7 +218,7 @@ public:
_outputStream.write((const char*)data, size);
data = data + size;
i += size;
} while (i < N && *(uint16_t*)data != 0);
} while (i + 1 < N && *(uint16_t*)data != 0);
current = current->prev;
} while (current != nullptr);
@ -295,20 +312,24 @@ class ProfileManager final
typedef profiler::guard_lock<profiler::spin_lock> guard_lock_t;
typedef std::map<profiler::thread_id_t, ThreadStorage> map_of_threads_stacks;
typedef std::vector<profiler::BlockDescriptor*> block_descriptors_t;
typedef std::vector<profiler::block_id_t> expired_ids_t;
typedef std::unordered_map<profiler::hashed_cstr, bool> blocks_enable_status_t;
map_of_threads_stacks m_threads;
block_descriptors_t m_descriptors;
expired_ids_t m_expiredDescriptors;
uint64_t m_usedMemorySize = 0;
profiler::spin_lock m_spin;
profiler::spin_lock m_storedSpin;
profiler::block_id_t m_idCounter = 0;
bool m_isEnabled = false;
map_of_threads_stacks m_threads;
block_descriptors_t m_descriptors;
blocks_enable_status_t m_blocksEnableStatus;
uint64_t m_usedMemorySize = 0;
profiler::spin_lock m_spin;
profiler::spin_lock m_storedSpin;
profiler::block_id_t m_idCounter = 0;
std::atomic_bool m_isEnabled;
std::atomic_bool m_isEventTracingEnabled;
#ifndef _WIN32
std::string m_csInfoFilename = "/tmp/cs_profiling_info.log";
#endif
uint32_t dumpBlocksToStream(profiler::OStream& _outputStream);
void setBlockEnabled(profiler::block_id_t _id, const profiler::hashed_stdstring& _key, bool _enabled);
public:
@ -316,14 +337,26 @@ public:
~ProfileManager();
template <class ... TArgs>
const profiler::BaseBlockDescriptor& addBlockDescriptor(bool _enabledByDefault, TArgs ... _args)
const profiler::BaseBlockDescriptor& addBlockDescriptor(bool _enabledByDefault, const char* _autogenUniqueId, TArgs ... _args)
{
auto desc = new profiler::BlockDescriptor(m_usedMemorySize, _enabledByDefault, _args...);
guard_lock_t lock(m_storedSpin);
desc->setId(m_idCounter++);
desc->m_id = m_idCounter++;
m_descriptors.emplace_back(desc);
blocks_enable_status_t::key_type key(_autogenUniqueId);
auto it = m_blocksEnableStatus.find(key);
if (it != m_blocksEnableStatus.end())
{
desc->m_enabled = it->second;
desc->m_pEnable = &it->second;
}
else
{
desc->m_pEnable = &m_blocksEnableStatus.emplace(key, desc->enabled()).first->second;
}
return *desc;
}
@ -332,8 +365,9 @@ public:
void endBlock();
void setEnabled(bool isEnable);
uint32_t dumpBlocksToFile(const char* filename);
const char* setThreadName(const char* name, const char* filename, const char* _funcname, int line);
const char* registerThread(const char* name);// , const char* filename, const char* _funcname, int line);
#ifndef _WIN32
void setContextSwitchLogFilename(const char* name)
{
m_csInfoFilename = name;
@ -343,16 +377,23 @@ public:
{
return m_csInfoFilename.c_str();
}
#endif
void beginContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id);
void storeContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id);
void endContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime);
void beginContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::thread_id_t _target_thread_id, const char* _target_process, bool _lockSpin = true);
void storeContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::thread_id_t _target_thread_id, bool _lockSpin = true);
void endContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime, bool _lockSpin = true);
private:
void markExpired(profiler::block_id_t _id);
ThreadStorage& threadStorage(profiler::thread_id_t _thread_id);
ThreadStorage* findThreadStorage(profiler::thread_id_t _thread_id);
ThreadStorage* _findThreadStorage(profiler::thread_id_t _thread_id);
ThreadStorage* findThreadStorage(profiler::thread_id_t _thread_id)
{
guard_lock_t lock(m_spin);
return _findThreadStorage(_thread_id);
}
};
#endif // EASY_PROFILER____MANAGER____H______

View File

@ -52,14 +52,6 @@
//////////////////////////////////////////////////////////////////////////
struct passthrough_hash {
template <class T> inline size_t operator () (T _value) const {
return static_cast<size_t>(_value);
}
};
//////////////////////////////////////////////////////////////////////////
namespace profiler {
void SerializedData::set(char* _data)
@ -90,7 +82,7 @@ namespace profiler {
#ifdef _WIN32
typedef ::std::unordered_map<::profiler::block_id_t, ::profiler::BlockStatistics*, passthrough_hash> StatsMap;
typedef ::std::unordered_map<::profiler::block_id_t, ::profiler::BlockStatistics*, ::profiler::passthrough_hash> StatsMap;
/** \note It is absolutely safe to use hashed_cstr (which simply stores pointer) because std::unordered_map,
which uses it as a key, exists only inside fillTreesFromFile function. */
@ -99,7 +91,7 @@ typedef ::std::unordered_map<::profiler::hashed_cstr, ::profiler::block_id_t> Id
#else
// TODO: Create optimized version of profiler::hashed_cstr for Linux too.
typedef ::std::unordered_map<::profiler::block_id_t, ::profiler::BlockStatistics*, passthrough_hash> StatsMap;
typedef ::std::unordered_map<::profiler::block_id_t, ::profiler::BlockStatistics*, ::profiler::passthrough_hash> StatsMap;
typedef ::std::unordered_map<::profiler::hashed_stdstring, ::profiler::block_id_t> IdMap;
#endif
@ -237,10 +229,12 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic<int>& progr
progress.store(static_cast<int>(10 * i / descriptors_memory_size));
}
typedef ::std::map<::profiler::thread_id_t, StatsMap> PerThreadStats;
typedef ::std::unordered_map<::profiler::thread_id_t, StatsMap, ::profiler::passthrough_hash> PerThreadStats;
PerThreadStats thread_statistics, parent_statistics, frame_statistics;
IdMap identification_table;
::std::vector<char> name;
i = 0;
uint32_t read_number = 0;
::profiler::block_index_t blocks_counter = 0;
@ -254,6 +248,15 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic<int>& progr
auto& root = threaded_trees[thread_id];
uint16_t name_size = 0;
inFile.read((char*)&name_size, sizeof(uint16_t));
if (name_size != 0)
{
name.resize(name_size);
inFile.read(name.data(), name_size);
root.thread_name = name.data();
}
uint32_t blocks_number_in_thread = 0;
inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(blocks_number_in_thread)));
auto threshold = read_number + blocks_number_in_thread;
@ -290,10 +293,6 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic<int>& progr
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)
@ -338,18 +337,13 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic<int>& progr
blocks.emplace_back();
::profiler::BlocksTree& tree = blocks.back();
tree.node = baseData;// new ::profiler::SerializedBlock(sz, data);
tree.node = baseData;
const auto block_index = blocks_counter++;
auto& per_parent_statistics = parent_statistics[thread_id];
auto& per_thread_statistics = thread_statistics[thread_id];
auto descriptor = descriptors[baseData->id()];
if (descriptor->type() == ::profiler::BLOCK_TYPE_THREAD_SIGN)
{
root.thread_name = tree.node->name();
}
if (*tree.node->name() != 0)
{
// If block has runtime name then generate new id for such block.