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

New flexible block statuses instead of ENABLED, DISABLED: OFF, ON, FORCE_ON, OFF_RECURSIVE, ON_WITHOUT_CHILDREN, FORCE_ON_WITHOUT_CHILDREN

This commit is contained in:
Victor Zarubkin 2016-09-20 22:57:34 +03:00
parent cd0ef96793
commit e49b6179ef
13 changed files with 440 additions and 255 deletions

View File

@ -42,7 +42,7 @@ along with this program.If not, see <http://www.gnu.org/licenses/>.
{ {
// some code ... // some code ...
EASY_BLOCK("Check something", profiler::DISABLED); // Disabled block (There is possibility to enable this block later via GUI) EASY_BLOCK("Check something", profiler::OFF); // Disabled block (There is possibility to enable this block later via GUI)
if(something){ if(something){
EASY_BLOCK("Calling bar()"); // Block with default color EASY_BLOCK("Calling bar()"); // Block with default color
bar(); bar();
@ -53,8 +53,13 @@ along with this program.If not, see <http://www.gnu.org/licenses/>.
} }
EASY_END_BLOCK; // End of "Check something" block (Even if "Check something" is disabled, this EASY_END_BLOCK will not end any other block). EASY_END_BLOCK; // End of "Check something" block (Even if "Check something" is disabled, this EASY_END_BLOCK will not end any other block).
EASY_BLOCK("Some another block", profiler::colors::Blue, profiler::DISABLED); // Disabled block with Blue color EASY_BLOCK("Some another block", profiler::colors::Blue, profiler::ON_WITHOUT_CHILDREN); // Block with Blue color without
// some another code... // some another code...
EASY_BLOCK("Calculate sum"); // This block will not be profiled because it's parent is ON_WITHOUT_CHILDREN
int sum = 0;
for (int i = 0; i < 10; ++i)
sum += i;
EASY_END_BLOCK; // End of "Calculate sum" block
} }
\endcode \endcode
@ -83,7 +88,7 @@ Block will be automatically completed by destructor.
} }
void baz(){ void baz(){
EASY_FUNCTION(profiler::DISABLED); // Disabled block with name="baz" and default color (There is possibility to enable this block later via GUI) EASY_FUNCTION(profiler::FORCE_ON); // Force enabled block with name="baz" and default color (This block will be profiled even if it's parent is OFF_RECURSIVE)
// som code... // som code...
} }
\endcode \endcode
@ -158,8 +163,10 @@ will end previously opened EASY_BLOCK or EASY_FUNCTION.
*/ */
# define EASY_THREAD(name)\ # define EASY_THREAD(name)\
EASY_THREAD_LOCAL static const char* EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = nullptr;\ EASY_THREAD_LOCAL static const char* EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = nullptr;\
::profiler::ThreadGuard EASY_TOKEN_CONCATENATE(unique_profiler_thread_guard, __LINE__);\
if (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::registerThread(name); EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = ::profiler::registerThread(name,\
EASY_TOKEN_CONCATENATE(unique_profiler_thread_guard, __LINE__));
/** Macro for main thread registration. /** Macro for main thread registration.
@ -295,7 +302,7 @@ Otherwise, no log messages will be printed.
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
class ProfileManager; class ProfileManager;
class ThreadStorage; struct ThreadStorage;
namespace profiler { namespace profiler {
@ -323,16 +330,17 @@ namespace profiler {
class PROFILER_API BaseBlockDescriptor class PROFILER_API BaseBlockDescriptor
{ {
friend ::ProfileManager; friend ::ProfileManager;
friend ::ThreadStorage;
protected: protected:
block_id_t m_id; ///< This descriptor id (We can afford this spending because there are much more blocks than descriptors) block_id_t m_id; ///< This descriptor id (We can afford this spending because there are much more blocks than descriptors)
int m_line; ///< Line number in the source file int m_line; ///< Line number in the source file
color_t m_color; ///< Color of the block packed into 1-byte structure color_t m_color; ///< Color of the block packed into 1-byte structure
block_type_t m_type; ///< Type of the block (See BlockType) block_type_t m_type; ///< Type of the block (See BlockType)
bool m_enabled; ///< If false then blocks with such id() will not be stored by profiler during profile session EasyBlockStatus m_status; ///< If false then blocks with such id() will not be stored by profiler during profile session
BaseBlockDescriptor(block_id_t _id, bool _enabled, int _line, block_type_t _block_type, color_t _color); BaseBlockDescriptor(block_id_t _id, EasyBlockStatus _status, int _line, block_type_t _block_type, color_t _color);
public: public:
@ -340,7 +348,7 @@ namespace profiler {
inline int line() const { return m_line; } inline int line() const { return m_line; }
inline color_t color() const { return m_color; } inline color_t color() const { return m_color; }
inline block_type_t type() const { return m_type; } inline block_type_t type() const { return m_type; }
inline bool enabled() const { return m_enabled; } inline EasyBlockStatus status() const { return m_status; }
}; // END of class BaseBlockDescriptor. }; // END of class BaseBlockDescriptor.
@ -381,17 +389,17 @@ namespace profiler {
{ {
friend ::ProfileManager; friend ::ProfileManager;
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_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 const char* m_filename; ///< Source file name where this block is declared
bool* m_pEnable; ///< Pointer to the enable flag in unordered_map EasyBlockStatus* m_pStatus; ///< Pointer to the enable flag in unordered_map
uint16_t m_size; ///< Used memory size uint16_t m_size; ///< Used memory size
bool m_expired; ///< Is this descriptor expired bool m_expired; ///< Is this descriptor expired
BlockDescriptor(bool _enabled, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color); BlockDescriptor(EasyBlockStatus _status, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color);
public: public:
BlockDescriptor(block_id_t _id, bool _enabled, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color); BlockDescriptor(block_id_t _id, EasyBlockStatus _status, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color);
inline const char* name() const { inline const char* name() const {
return m_name; return m_name;
@ -410,8 +418,8 @@ namespace profiler {
friend ::ProfileManager; friend ::ProfileManager;
friend ::ThreadStorage; friend ::ThreadStorage;
const char* m_name; const char* m_name;
bool m_enabled; EasyBlockStatus m_status;
private: private:
@ -420,7 +428,8 @@ namespace profiler {
void finish(); void finish();
void finish(timestamp_t _time); void finish(timestamp_t _time);
inline bool finished() const { return m_end >= m_begin; } inline bool finished() const { return m_end >= m_begin; }
inline bool enabled() const { return m_enabled; } inline EasyBlockStatus status() const { return m_status; }
inline void setStatus(EasyBlockStatus _status) { m_status = _status; }
public: public:
@ -459,6 +468,13 @@ namespace profiler {
}; // END of class BlockDescRef. }; // END of class BlockDescRef.
class PROFILER_API ThreadGuard final {
friend ::ProfileManager;
thread_id_t m_id = 0;
public:
~ThreadGuard();
};
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Core API // Core API
// Note: it is better to use macros defined above than a direct calls to API. // Note: it is better to use macros defined above than a direct calls to API.
@ -472,7 +488,7 @@ namespace profiler {
\ingroup profiler \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); PROFILER_API const BaseBlockDescriptor* registerDescription(EasyBlockStatus _status, 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. /** Stores event in the blocks list.
@ -517,7 +533,7 @@ namespace profiler {
\ingroup profiler \ingroup profiler
*/ */
PROFILER_API const char* registerThread(const char* _name); PROFILER_API const char* registerThread(const char* _name, ThreadGuard&);
/** Enable or disable event tracing. /** Enable or disable event tracing.
@ -555,15 +571,11 @@ namespace profiler {
PROFILER_API const char* getContextSwitchLogFilename(); PROFILER_API const char* getContextSwitchLogFilename();
#endif #endif
PROFILER_API void startListenSignalToCapture(); PROFILER_API void startListenSignalToCapture();
PROFILER_API void stopListenSignalToCapture(); PROFILER_API void stopListenSignalToCapture();
} }
inline void setEnabled(::profiler::EasyEnableFlag _isEnable) {
setEnabled(_isEnable == ::profiler::ENABLED);
}
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
} // END of namespace profiler. } // END of namespace profiler.

View File

@ -80,10 +80,13 @@
namespace profiler { namespace profiler {
enum EasyEnableFlag : uint8_t enum EasyBlockStatus : uint8_t {
{ OFF = 0, ///< The block is OFF
DISABLED = 0, ON = 1, ///< The block is ON (but if it's parent block is off recursively then this block will be off too)
ENABLED = 1 FORCE_ON = ON | 2, ///< The block is ALWAYS ON (even if it's parent has turned off all children)
OFF_RECURSIVE = 4, ///< The block is OFF and all of it's children by call-stack are also OFF.
ON_WITHOUT_CHILDREN = ON | OFF_RECURSIVE, ///< The block is ON but all of it's children are OFF.
FORCE_ON_WITHOUT_CHILDREN = FORCE_ON | OFF_RECURSIVE, ///< The block is ALWAYS ON but all of it's children are OFF.
}; };
struct passthrough_hash final { struct passthrough_hash final {
@ -126,7 +129,7 @@ namespace profiler {
} }
template <class ... TArgs> template <class ... TArgs>
inline color_t extract_color(::profiler::EasyEnableFlag, TArgs...) { inline color_t extract_color(::profiler::EasyBlockStatus, TArgs...) {
return ::profiler::colors::Default; return ::profiler::colors::Default;
} }
@ -148,24 +151,24 @@ namespace profiler {
//*********************************************** //***********************************************
inline bool extract_enable_flag() { inline EasyBlockStatus extract_enable_flag() {
return true; return ::profiler::ON;
} }
template <class T, class ... TArgs> template <class T, class ... TArgs>
inline bool extract_enable_flag(T, ::profiler::EasyEnableFlag _flag, TArgs...) { inline EasyBlockStatus extract_enable_flag(T, ::profiler::EasyBlockStatus _flag, TArgs...) {
return _flag == ::profiler::ENABLED; return _flag;
} }
template <class ... TArgs> template <class ... TArgs>
inline bool extract_enable_flag(::profiler::EasyEnableFlag _flag, TArgs...) { inline EasyBlockStatus extract_enable_flag(::profiler::EasyBlockStatus _flag, TArgs...) {
return _flag == ::profiler::ENABLED; return _flag;
} }
template <class ... TArgs> template <class ... TArgs>
inline bool extract_enable_flag(TArgs...) { inline EasyBlockStatus extract_enable_flag(TArgs...) {
static_assert(sizeof...(TArgs) < 2, "No EasyEnableFlag in arguments list for EASY_BLOCK(name, ...)!"); static_assert(sizeof...(TArgs) < 2, "No EasyBlockStatus in arguments list for EASY_BLOCK(name, ...)!");
return true; return ::profiler::ON;
} }
//*********************************************** //***********************************************

View File

@ -21,8 +21,6 @@ along with this program.If not, see <http://www.gnu.org/licenses/>.
#include "profiler/profiler.h" #include "profiler/profiler.h"
class ThreadStorage;
namespace profiler { namespace profiler {
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -69,9 +67,9 @@ namespace profiler {
return name() + m_nameLength; return name() + m_nameLength;
} }
inline void setEnabled(bool _enabled) inline void setStatus(EasyBlockStatus _status)
{ {
m_enabled = _enabled; m_status = _status;
} }
private: private:

View File

@ -38,6 +38,7 @@
************************************************************************/ ************************************************************************/
#include <QMenu> #include <QMenu>
#include <QAction>
#include <QHeaderView> #include <QHeaderView>
#include <QContextMenuEvent> #include <QContextMenuEvent>
#include <QSignalBlocker> #include <QSignalBlocker>
@ -442,18 +443,24 @@ void EasyTreeWidget::contextMenuEvent(QContextMenuEvent* _event)
} }
} }
auto id = item->block().node->id(); const auto& desc = easyDescriptor(item->block().node->id());
action = menu.addAction("Block enabled"); auto submenu = menu.addMenu("Block status");
action->setCheckable(true);
action->setChecked(easyDescriptor(id).enabled());
action->setData(id);
connect(action, &QAction::triggered, this, &This::onBlockEnableDisable);
if (action->isChecked()) { #define ADD_STATUS_ACTION(NameValue, StatusValue)\
auto f = action->font(); action = submenu->addAction(NameValue);\
f.setBold(true); action->setCheckable(true);\
action->setFont(f); action->setChecked(desc.status() == StatusValue);\
} action->setData(static_cast<quint32>(StatusValue));\
connect(action, &QAction::triggered, this, &This::onBlockStatusChangeClicked)
ADD_STATUS_ACTION("Off", ::profiler::OFF);
ADD_STATUS_ACTION("On", ::profiler::ON);
ADD_STATUS_ACTION("Force-On", ::profiler::FORCE_ON);
ADD_STATUS_ACTION("Off-recursive", ::profiler::OFF_RECURSIVE);
ADD_STATUS_ACTION("On-without-children", ::profiler::ON_WITHOUT_CHILDREN);
ADD_STATUS_ACTION("Force-On-without-children", ::profiler::FORCE_ON_WITHOUT_CHILDREN);
#undef ADD_STATUS_ACTION
} }
menu.addSeparator(); menu.addSeparator();
@ -579,14 +586,21 @@ void EasyTreeWidget::onExpandAllChildrenClicked(bool)
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
void EasyTreeWidget::onBlockEnableDisable(bool _checked) void EasyTreeWidget::onBlockStatusChangeClicked(bool _checked)
{ {
if (!_checked)
return;
auto item = static_cast<EasyTreeWidgetItem*>(currentItem());
if (item == nullptr)
return;
auto action = qobject_cast<QAction*>(sender()); auto action = qobject_cast<QAction*>(sender());
if (action != nullptr) if (action != nullptr)
{ {
auto id = action->data().toUInt(); auto& desc = easyDescriptor(item->block().node->id());
easyDescriptor(id).setEnabled(_checked); desc.setStatus(static_cast<::profiler::EasyBlockStatus>(action->data().toUInt()));
emit EASY_GLOBALS.events.enableStatusChanged(id, _checked); emit EASY_GLOBALS.events.blockStatusChanged(desc.id(), desc.status());
} }
} }

View File

@ -40,7 +40,6 @@
#define EASY__TREE_WIDGET__H_ #define EASY__TREE_WIDGET__H_
#include <QTreeWidget> #include <QTreeWidget>
#include <QAction>
#include <QTimer> #include <QTimer>
#include "tree_widget_loader.h" #include "tree_widget_loader.h"
#include "profiler/reader.h" #include "profiler/reader.h"
@ -108,7 +107,7 @@ private slots:
void onSelectedBlockChange(uint32_t _block_index); void onSelectedBlockChange(uint32_t _block_index);
void onBlockEnableDisable(bool _checked); void onBlockStatusChangeClicked(bool);
void resizeColumnsToContents(); void resizeColumnsToContents();

View File

@ -31,6 +31,7 @@
#include <QMenu> #include <QMenu>
#include <QAction> #include <QAction>
#include <QActionGroup>
#include <QHeaderView> #include <QHeaderView>
#include <QString> #include <QString>
#include <QContextMenuEvent> #include <QContextMenuEvent>
@ -63,14 +64,92 @@
enum DescColumns enum DescColumns
{ {
DESC_COL_FILE_LINE = 0, DESC_COL_FILE_LINE = 0,
DESC_COL_TYPE,
DESC_COL_NAME, DESC_COL_NAME,
DESC_COL_STATUS, DESC_COL_STATUS,
DESC_COL_COLUMNS_NUMBER DESC_COL_COLUMNS_NUMBER
}; };
const auto ENABLED_COLOR = ::profiler::colors::LightGreen900; //////////////////////////////////////////////////////////////////////////
const auto DISABLED_COLOR = ::profiler::colors::DarkRed;
::profiler::EasyBlockStatus nextStatus(::profiler::EasyBlockStatus _status)
{
switch (_status)
{
case ::profiler::OFF:
return ::profiler::ON;
case ::profiler::ON:
return ::profiler::FORCE_ON;
case ::profiler::FORCE_ON:
return ::profiler::OFF_RECURSIVE;
case ::profiler::OFF_RECURSIVE:
return ::profiler::ON_WITHOUT_CHILDREN;
case ::profiler::ON_WITHOUT_CHILDREN:
return ::profiler::FORCE_ON_WITHOUT_CHILDREN;
case ::profiler::FORCE_ON_WITHOUT_CHILDREN:
return ::profiler::OFF;
}
return ::profiler::OFF;
}
const char* statusText(::profiler::EasyBlockStatus _status)
{
switch (_status)
{
case ::profiler::OFF:
return "OFF";
case ::profiler::ON:
return "ON";
case ::profiler::FORCE_ON:
return "FORCE_ON";
case ::profiler::OFF_RECURSIVE:
return "OFF_RECURSIVE";
case ::profiler::ON_WITHOUT_CHILDREN:
return "ON_WITHOUT_CHILDREN";
case ::profiler::FORCE_ON_WITHOUT_CHILDREN:
return "FORCE_ON_WITHOUT_CHILDREN";
}
return "";
}
::profiler::color_t statusColor(::profiler::EasyBlockStatus _status)
{
switch (_status)
{
case ::profiler::OFF:
return ::profiler::colors::Red900;
case ::profiler::ON:
return ::profiler::colors::LightGreen900;
case ::profiler::FORCE_ON:
return ::profiler::colors::LightGreen900;
case ::profiler::OFF_RECURSIVE:
return ::profiler::colors::Red900;
case ::profiler::ON_WITHOUT_CHILDREN:
return ::profiler::colors::Lime900;
case ::profiler::FORCE_ON_WITHOUT_CHILDREN:
return ::profiler::colors::Lime900;
}
return ::profiler::colors::Black;
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -117,12 +196,13 @@ EasyDescTreeWidget::EasyDescTreeWidget(QWidget* _parent)
auto header_item = new QTreeWidgetItem(); auto header_item = new QTreeWidgetItem();
header_item->setText(DESC_COL_FILE_LINE, "File/Line"); header_item->setText(DESC_COL_FILE_LINE, "File/Line");
header_item->setText(DESC_COL_TYPE, "Type");
header_item->setText(DESC_COL_NAME, "Name"); header_item->setText(DESC_COL_NAME, "Name");
header_item->setText(DESC_COL_STATUS, "Status"); header_item->setText(DESC_COL_STATUS, "Status");
setHeaderItem(header_item); setHeaderItem(header_item);
connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedBlockChanged, this, &This::onSelectedBlockChange); connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedBlockChanged, this, &This::onSelectedBlockChange);
connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::enableStatusChanged, this, &This::onEnableStatusChange); connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::blockStatusChanged, this, &This::onBlockStatusChange);
connect(this, &Parent::itemExpanded, this, &This::onItemExpand); connect(this, &Parent::itemExpanded, this, &This::onItemExpand);
connect(this, &Parent::itemDoubleClicked, this, &This::onDoubleClick); connect(this, &Parent::itemDoubleClicked, this, &This::onDoubleClick);
connect(this, &Parent::currentItemChanged, this, &This::onCurrentItemChange); connect(this, &Parent::currentItemChanged, this, &This::onCurrentItemChange);
@ -155,12 +235,40 @@ void EasyDescTreeWidget::contextMenuEvent(QContextMenuEvent* _event)
auto header_item = headerItem(); auto header_item = headerItem();
for (int i = 0; i < DESC_COL_STATUS; ++i) for (int i = 0; i < DESC_COL_STATUS; ++i)
{ {
action = new QAction(header_item->text(i), nullptr); if (i == DESC_COL_TYPE)
continue;
action = submenu->addAction(header_item->text(i));
action->setData(i);
action->setCheckable(true); action->setCheckable(true);
if (i == m_searchColumn) if (i == m_searchColumn)
action->setChecked(true); action->setChecked(true);
connect(action, &QAction::triggered, this, &This::onSearchColumnChange); connect(action, &QAction::triggered, this, &This::onSearchColumnChange);
submenu->addAction(action); }
auto item = currentItem();
if (item != nullptr && item->parent() != nullptr && currentColumn() >= DESC_COL_TYPE)
{
const auto& desc = easyDescriptor(static_cast<EasyDescWidgetItem*>(item)->desc());
menu.addSeparator();
auto submenu = menu.addMenu("Change status");
#define ADD_STATUS_ACTION(NameValue, StatusValue)\
action = submenu->addAction(NameValue);\
action->setCheckable(true);\
action->setChecked(desc.status() == StatusValue);\
action->setData(static_cast<quint32>(StatusValue));\
connect(action, &QAction::triggered, this, &This::onBlockStatusChangeClicked)
ADD_STATUS_ACTION("Off", ::profiler::OFF);
ADD_STATUS_ACTION("On", ::profiler::ON);
ADD_STATUS_ACTION("Force-On", ::profiler::FORCE_ON);
ADD_STATUS_ACTION("Off-recursive", ::profiler::OFF_RECURSIVE);
ADD_STATUS_ACTION("On-without-children", ::profiler::ON_WITHOUT_CHILDREN);
ADD_STATUS_ACTION("Force-On-without-children", ::profiler::FORCE_ON_WITHOUT_CHILDREN);
#undef ADD_STATUS_ACTION
} }
menu.exec(QCursor::pos()); menu.exec(QCursor::pos());
@ -254,6 +362,8 @@ void EasyDescTreeWidget::build()
{ {
p.item = new QTreeWidgetItem(); p.item = new QTreeWidgetItem();
p.item->setText(DESC_COL_FILE_LINE, desc->file()); p.item->setText(DESC_COL_FILE_LINE, desc->file());
p.item->setText(DESC_COL_TYPE, "F");
p.item->setToolTip(DESC_COL_TYPE, "File");
} }
auto it = p.children.find(desc->line()); auto it = p.children.find(desc->line());
@ -264,21 +374,20 @@ void EasyDescTreeWidget::build()
item->setData(DESC_COL_FILE_LINE, Qt::UserRole, desc->line()); item->setData(DESC_COL_FILE_LINE, Qt::UserRole, desc->line());
item->setText(DESC_COL_NAME, desc->name()); item->setText(DESC_COL_NAME, desc->name());
item->setFont(DESC_COL_STATUS, f); if (desc->type() == ::profiler::BLOCK_TYPE_BLOCK)
QBrush brush;
if (desc->enabled())
{ {
item->setText(DESC_COL_STATUS, "ON"); item->setText(DESC_COL_TYPE, "B");
brush.setColor(QColor::fromRgba(ENABLED_COLOR)); item->setToolTip(DESC_COL_TYPE, "Block");
} }
else else
{ {
item->setText(DESC_COL_STATUS, "OFF"); item->setText(DESC_COL_TYPE, "E");
brush.setColor(QColor::fromRgba(DISABLED_COLOR)); item->setToolTip(DESC_COL_TYPE, "Event");
} }
item->setForeground(DESC_COL_STATUS, brush); item->setFont(DESC_COL_STATUS, f);
item->setText(DESC_COL_STATUS, statusText(desc->status()));
item->setForeground(DESC_COL_STATUS, QColor::fromRgba(statusColor(desc->status())));
m_items[id] = item; m_items[id] = item;
} }
@ -312,29 +421,17 @@ void EasyDescTreeWidget::onItemExpand(QTreeWidgetItem*)
void EasyDescTreeWidget::onDoubleClick(QTreeWidgetItem* _item, int _column) void EasyDescTreeWidget::onDoubleClick(QTreeWidgetItem* _item, int _column)
{ {
if (_column >= DESC_COL_NAME && _item->parent() != nullptr) if (_column >= DESC_COL_TYPE && _item->parent() != nullptr)
{ {
auto item = static_cast<EasyDescWidgetItem*>(_item); auto item = static_cast<EasyDescWidgetItem*>(_item);
auto& desc = easyDescriptor(item->desc()); auto& desc = easyDescriptor(item->desc());
desc.setStatus(nextStatus(desc.status()));
QBrush brush; item->setText(DESC_COL_STATUS, statusText(desc.status()));
if (desc.enabled()) item->setForeground(DESC_COL_STATUS, QColor::fromRgba(statusColor(desc.status())));
{
desc.setEnabled(false);
item->setText(DESC_COL_STATUS, "OFF");
brush.setColor(QColor::fromRgba(DISABLED_COLOR));
}
else
{
desc.setEnabled(true);
item->setText(DESC_COL_STATUS, "ON");
brush.setColor(QColor::fromRgba(ENABLED_COLOR));
}
item->setForeground(DESC_COL_STATUS, brush);
m_bLocked = true; m_bLocked = true;
emit EASY_GLOBALS.events.enableStatusChanged(item->desc(), desc.enabled()); emit EASY_GLOBALS.events.blockStatusChanged(desc.id(), desc.status());
m_bLocked = false; m_bLocked = false;
} }
} }
@ -361,7 +458,30 @@ void EasyDescTreeWidget::onCurrentItemChange(QTreeWidgetItem* _item, QTreeWidget
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
void EasyDescTreeWidget::onEnableStatusChange(::profiler::block_id_t _id, bool _enabled) void EasyDescTreeWidget::onBlockStatusChangeClicked(bool _checked)
{
if (!_checked)
return;
auto item = currentItem();
if (item == nullptr || item->parent() == nullptr)
return;
auto action = qobject_cast<QAction*>(sender());
if (action != nullptr)
{
auto& desc = easyDescriptor(static_cast<EasyDescWidgetItem*>(item)->desc());
desc.setStatus(static_cast<::profiler::EasyBlockStatus>(action->data().toUInt()));
item->setText(DESC_COL_STATUS, statusText(desc.status()));
item->setForeground(DESC_COL_STATUS, QColor::fromRgba(statusColor(desc.status())));
m_bLocked = true;
emit EASY_GLOBALS.events.blockStatusChanged(desc.id(), desc.status());
m_bLocked = false;
}
}
void EasyDescTreeWidget::onBlockStatusChange(::profiler::block_id_t _id, ::profiler::EasyBlockStatus _status)
{ {
if (m_bLocked) if (m_bLocked)
return; return;
@ -370,19 +490,9 @@ void EasyDescTreeWidget::onEnableStatusChange(::profiler::block_id_t _id, bool _
if (item == nullptr) if (item == nullptr)
return; return;
QBrush brush; auto& desc = easyDescriptor(item->desc());
if (_enabled) item->setText(DESC_COL_STATUS, statusText(desc.status()));
{ item->setForeground(DESC_COL_STATUS, QColor::fromRgba(statusColor(desc.status())));
item->setText(DESC_COL_STATUS, "ON");
brush.setColor(QColor::fromRgba(ENABLED_COLOR));
}
else
{
item->setText(DESC_COL_STATUS, "OFF");
brush.setColor(QColor::fromRgba(DISABLED_COLOR));
}
item->setForeground(DESC_COL_STATUS, brush);
} }
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////

View File

@ -107,11 +107,12 @@ public slots:
private slots: private slots:
void onSearchColumnChange(bool); void onSearchColumnChange(bool);
void onBlockStatusChangeClicked(bool);
void onCurrentItemChange(QTreeWidgetItem* _item, QTreeWidgetItem* _prev); void onCurrentItemChange(QTreeWidgetItem* _item, QTreeWidgetItem* _prev);
void onItemExpand(QTreeWidgetItem* _item); void onItemExpand(QTreeWidgetItem* _item);
void onDoubleClick(QTreeWidgetItem* _item, int _column); void onDoubleClick(QTreeWidgetItem* _item, int _column);
void onSelectedBlockChange(uint32_t _block_index); void onSelectedBlockChange(uint32_t _block_index);
void onEnableStatusChange(::profiler::block_id_t _id, bool _enabled); void onBlockStatusChange(::profiler::block_id_t _id, ::profiler::EasyBlockStatus _status);
void resizeColumnsToContents(); void resizeColumnsToContents();
private: private:

View File

@ -52,7 +52,7 @@ namespace profiler_gui {
void itemsExpandStateChanged(); void itemsExpandStateChanged();
void drawBordersChanged(); void drawBordersChanged();
void chronoPositionChanged(); void chronoPositionChanged();
void enableStatusChanged(::profiler::block_id_t _id, bool _enabled); void blockStatusChanged(::profiler::block_id_t _id, ::profiler::EasyBlockStatus _status);
}; // END of class EasyGlobalSignals. }; // END of class EasyGlobalSignals.

View File

@ -14,8 +14,8 @@ std::mutex cv_m;
int g_i = 0; int g_i = 0;
int OBJECTS = 500; int OBJECTS = 500;
int RENDER_STEPS = 1600; int MODELLING_STEPS = 1500;
int MODELLING_STEPS = 1000; int RENDER_STEPS = 1500;
int RESOURCE_LOADING_COUNT = 50; int RESOURCE_LOADING_COUNT = 50;
void localSleep(int magic=200000) void localSleep(int magic=200000)
@ -105,7 +105,7 @@ void prepareRender(){
int multPhys(int i) int multPhys(int i)
{ {
EASY_FUNCTION(profiler::colors::Red700, profiler::DISABLED); EASY_FUNCTION(profiler::colors::Red700, profiler::ON);
return i * i * i * i / 100; return i * i * i * i / 100;
} }
@ -146,7 +146,7 @@ void modellingThread(){
//std::unique_lock<std::mutex> lk(cv_m); //std::unique_lock<std::mutex> lk(cv_m);
//cv.wait(lk, []{return g_i == 1; }); //cv.wait(lk, []{return g_i == 1; });
EASY_THREAD("Modelling"); EASY_THREAD("Modelling");
for (int i = 0; i < RENDER_STEPS; i++){ for (int i = 0; i < MODELLING_STEPS; i++){
modellingStep(); modellingStep();
localSleep(1200000); localSleep(1200000);
//std::this_thread::sleep_for(std::chrono::milliseconds(20)); //std::this_thread::sleep_for(std::chrono::milliseconds(20));
@ -157,7 +157,7 @@ void renderThread(){
//std::unique_lock<std::mutex> lk(cv_m); //std::unique_lock<std::mutex> lk(cv_m);
//cv.wait(lk, []{return g_i == 1; }); //cv.wait(lk, []{return g_i == 1; });
EASY_THREAD("Render"); EASY_THREAD("Render");
for (int i = 0; i < MODELLING_STEPS; i++){ for (int i = 0; i < RENDER_STEPS; i++){
frame(); frame();
localSleep(1200000); localSleep(1200000);
//std::this_thread::sleep_for(std::chrono::milliseconds(20)); //std::this_thread::sleep_for(std::chrono::milliseconds(20));
@ -172,34 +172,27 @@ int main(int argc, char* argv[])
OBJECTS = std::atoi(argv[1]); OBJECTS = std::atoi(argv[1]);
} }
if (argc > 2 && argv[2]){ if (argc > 2 && argv[2]){
RENDER_STEPS = std::atoi(argv[2]); MODELLING_STEPS = std::atoi(argv[2]);
} }
if (argc > 3 && argv[3]){ if (argc > 3 && argv[3]){
MODELLING_STEPS = std::atoi(argv[3]); RENDER_STEPS = std::atoi(argv[3]);
} }
if (argc > 4 && argv[4]){ if (argc > 4 && argv[4]){
RESOURCE_LOADING_COUNT = std::atoi(argv[4]); RESOURCE_LOADING_COUNT = std::atoi(argv[4]);
} }
std::cout << "Objects count: " << OBJECTS << std::endl; std::cout << "Objects count: " << OBJECTS << std::endl;
std::cout << "Render steps: " << RENDER_STEPS << std::endl; std::cout << "Render steps: " << MODELLING_STEPS << std::endl;
std::cout << "Modelling steps: " << MODELLING_STEPS << std::endl; std::cout << "Modelling steps: " << RENDER_STEPS << std::endl;
std::cout << "Resource loading count: " << RESOURCE_LOADING_COUNT << std::endl; std::cout << "Resource loading count: " << RESOURCE_LOADING_COUNT << std::endl;
auto start = std::chrono::system_clock::now(); auto start = std::chrono::system_clock::now();
EASY_PROFILER_ENABLE; EASY_PROFILER_ENABLE;
EASY_MAIN_THREAD; EASY_MAIN_THREAD;
profiler::startListenSignalToCapture(); profiler::startListenSignalToCapture();
//one();
//one();
/**/
std::vector<std::thread> threads; std::vector<std::thread> threads;
for (int i=0; i < 3; i++) {
std::thread render = std::thread(renderThread);
std::thread modelling = std::thread(modellingThread);
for(int i=0; i < 3; i++){
threads.emplace_back(std::thread(loadingResourcesThread)); threads.emplace_back(std::thread(loadingResourcesThread));
threads.emplace_back(std::thread(renderThread)); threads.emplace_back(std::thread(renderThread));
threads.emplace_back(std::thread(modellingThread)); threads.emplace_back(std::thread(modellingThread));
@ -210,16 +203,10 @@ int main(int argc, char* argv[])
cv_m.unlock(); cv_m.unlock();
cv.notify_all(); cv.notify_all();
for (int i = 0; i < RENDER_STEPS; ++i) { modellingThread();
modellingStep();
localSleep(1200000);
}
render.join();
modelling.join();
for(auto& t : threads) for(auto& t : threads)
t.join(); t.join();
/**/
auto end = std::chrono::system_clock::now(); auto end = std::chrono::system_clock::now();
auto elapsed = auto elapsed =

View File

@ -23,35 +23,11 @@
* : You should have received a copy of the GNU General Public License * : You should have received a copy of the GNU General Public License
* : along with this program.If not, see <http://www.gnu.org/licenses/>. * : along with this program.If not, see <http://www.gnu.org/licenses/>.
************************************************************************/ ************************************************************************/
#include "profiler/profiler.h"
#include "profile_manager.h" #include "profile_manager.h"
#include <ctime>
#include <chrono>
#include <thread>
using namespace profiler; using namespace profiler;
#ifdef _WIN32
decltype(LARGE_INTEGER::QuadPart) CPU_FREQUENCY = ([](){ LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); return freq.QuadPart; })();
#endif
inline timestamp_t getCurrentTime()
{
#ifdef _WIN32
//see https://msdn.microsoft.com/library/windows/desktop/dn553408(v=vs.85).aspx
LARGE_INTEGER elapsedMicroseconds;
if (!QueryPerformanceCounter(&elapsedMicroseconds))
return 0;
//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;
time_point = std::chrono::time_point_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now());
return time_point.time_since_epoch().count();
#endif
}
BaseBlockData::BaseBlockData(timestamp_t _begin_time, block_id_t _descriptor_id) BaseBlockData::BaseBlockData(timestamp_t _begin_time, block_id_t _descriptor_id)
: m_begin(_begin_time) : m_begin(_begin_time)
, m_end(0) , m_end(0)
@ -63,23 +39,25 @@ BaseBlockData::BaseBlockData(timestamp_t _begin_time, block_id_t _descriptor_id)
Block::Block(Block&& that) Block::Block(Block&& that)
: BaseBlockData(that.m_begin, that.m_id) : BaseBlockData(that.m_begin, that.m_id)
, m_name(that.m_name) , m_name(that.m_name)
, m_enabled(that.m_enabled) , m_status(that.m_status)
{ {
m_end = that.m_end; m_end = that.m_end;
} }
Block::Block(const BaseBlockDescriptor& _descriptor, const char* _runtimeName) Block::Block(const BaseBlockDescriptor& _descriptor, const char* _runtimeName)
: BaseBlockData(_descriptor.enabled() ? getCurrentTime() : 1ULL, _descriptor.id()) : BaseBlockData(1ULL, _descriptor.id())
, m_name(_runtimeName) , m_name(_runtimeName)
, m_enabled(_descriptor.enabled()) , m_status(_descriptor.status())
{ {
} }
Block::Block(timestamp_t _begin_time, block_id_t _descriptor_id, const char* _runtimeName) Block::Block(timestamp_t _begin_time, block_id_t _descriptor_id, const char* _runtimeName)
: BaseBlockData(_begin_time, _descriptor_id) : BaseBlockData(_begin_time, _descriptor_id)
, m_name(_runtimeName) , m_name(_runtimeName)
, m_enabled(true) , m_status(::profiler::ON)
{ {
} }
void Block::start() void Block::start()

View File

@ -103,7 +103,7 @@ namespace profiler {
if (sizeof(CSwitch) != _traceEvent->UserDataLength) if (sizeof(CSwitch) != _traceEvent->UserDataLength)
return; return;
EASY_FUNCTION(::profiler::colors::White, ::profiler::DISABLED); EASY_FUNCTION(::profiler::colors::White, ::profiler::OFF);
auto _contextSwitchEvent = reinterpret_cast<CSwitch*>(_traceEvent->UserData); auto _contextSwitchEvent = reinterpret_cast<CSwitch*>(_traceEvent->UserData);
const auto time = static_cast<::profiler::timestamp_t>(_traceEvent->EventHeader.TimeStamp.QuadPart); const auto time = static_cast<::profiler::timestamp_t>(_traceEvent->EventHeader.TimeStamp.QuadPart);

View File

@ -23,41 +23,38 @@
* : You should have received a copy of the GNU General Public License * : You should have received a copy of the GNU General Public License
* : along with this program.If not, see <http://www.gnu.org/licenses/>. * : along with this program.If not, see <http://www.gnu.org/licenses/>.
************************************************************************/ ************************************************************************/
#include <algorithm>
#include <fstream>
#include "profile_manager.h" #include "profile_manager.h"
#include "profiler/serialized_block.h" #include "profiler/serialized_block.h"
#include "profiler/easy_net.h" #include "profiler/easy_net.h"
#include "profiler/easy_socket.h" #include "profiler/easy_socket.h"
#include "event_trace_win.h" #include "event_trace_win.h"
#include <thread>
#include <string.h>
#include <algorithm>
#include <fstream>
#include "profiler/serialized_block.h"
#include "profile_manager.h"
#include "event_trace_win.h"
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
using namespace profiler; using namespace profiler;
#ifdef _WIN32 const uint8_t FORCE_ON_FLAG = profiler::FORCE_ON & ~profiler::ON;
extern decltype(LARGE_INTEGER::QuadPart) CPU_FREQUENCY;
#endif
extern timestamp_t getCurrentTime();
//auto& MANAGER = ProfileManager::instance(); //auto& MANAGER = ProfileManager::instance();
#define MANAGER ProfileManager::instance() #define MANAGER ProfileManager::instance()
EASY_THREAD_LOCAL static ::ThreadStorage* THREAD_STORAGE = nullptr;
#ifdef _WIN32
decltype(LARGE_INTEGER::QuadPart) CPU_FREQUENCY = ([](){ LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); return freq.QuadPart; })();
#endif
//////////////////////////////////////////////////////////////////////////
extern "C" { extern "C" {
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) PROFILER_API const BaseBlockDescriptor* registerDescription(EasyBlockStatus _status, const char* _autogenUniqueId, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color)
{ {
return MANAGER.addBlockDescriptor(_enabled, _autogenUniqueId, _name, _filename, _line, _block_type, _color); return MANAGER.addBlockDescriptor(_status, _autogenUniqueId, _name, _filename, _line, _block_type, _color);
} }
PROFILER_API void endBlock() PROFILER_API void endBlock()
@ -85,9 +82,9 @@ extern "C" {
return MANAGER.dumpBlocksToFile(filename); return MANAGER.dumpBlocksToFile(filename);
} }
PROFILER_API const char* registerThread(const char* name)//, const char* filename, const char* _funcname, int line) PROFILER_API const char* registerThread(const char* name, ThreadGuard& threadGuard)
{ {
return MANAGER.registerThread(name);// , filename, _funcname, line); return MANAGER.registerThread(name, threadGuard);
} }
PROFILER_API void setEventTracingEnabled(bool _isEnable) PROFILER_API void setEventTracingEnabled(bool _isEnable)
@ -138,31 +135,31 @@ SerializedBlock::SerializedBlock(const Block& block, uint16_t name_length)
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
BaseBlockDescriptor::BaseBlockDescriptor(block_id_t _id, bool _enabled, int _line, block_type_t _block_type, color_t _color) BaseBlockDescriptor::BaseBlockDescriptor(block_id_t _id, EasyBlockStatus _status, int _line, block_type_t _block_type, color_t _color)
: m_id(_id) : m_id(_id)
, m_line(_line) , m_line(_line)
, m_type(_block_type) , m_type(_block_type)
, m_color(_color) , m_color(_color)
, m_enabled(_enabled) , m_status(_status)
{ {
} }
BlockDescriptor::BlockDescriptor(block_id_t _id, bool _enabled, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color) BlockDescriptor::BlockDescriptor(block_id_t _id, EasyBlockStatus _status, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color)
: BaseBlockDescriptor(_id, _enabled, _line, _block_type, _color) : BaseBlockDescriptor(_id, _status, _line, _block_type, _color)
, m_name(_name) , m_name(_name)
, m_filename(_filename) , m_filename(_filename)
, m_pEnable(nullptr) , m_pStatus(nullptr)
, m_size(static_cast<uint16_t>(sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2)) , m_size(static_cast<uint16_t>(sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2))
, m_expired(false) , m_expired(false)
{ {
} }
BlockDescriptor::BlockDescriptor(bool _enabled, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color) BlockDescriptor::BlockDescriptor(EasyBlockStatus _status, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color)
: BaseBlockDescriptor(0, _enabled, _line, _block_type, _color) : BaseBlockDescriptor(0, _status, _line, _block_type, _color)
, m_name(_name) , m_name(_name)
, m_filename(_filename) , m_filename(_filename)
, m_pEnable(nullptr) , m_pStatus(nullptr)
, m_size(static_cast<uint16_t>(sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2)) , m_size(static_cast<uint16_t>(sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2))
, m_expired(false) , m_expired(false)
{ {
@ -178,14 +175,14 @@ BlockDescRef::~BlockDescRef()
void ThreadStorage::storeBlock(const profiler::Block& block) void ThreadStorage::storeBlock(const profiler::Block& block)
{ {
#if EASY_MEASURE_STORAGE_EXPAND != 0 #if EASY_MEASURE_STORAGE_EXPAND != 0
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); static const auto desc = MANAGER.addBlockDescriptor(EASY_STORAGE_EXPAND_ENABLED ? profiler::ON : profiler::OFF, EASY_UNIQUE_LINE_ID, "EasyProfiler.ExpandStorage", __FILE__, __LINE__, profiler::BLOCK_TYPE_BLOCK, profiler::colors::White);
#endif #endif
auto name_length = static_cast<uint16_t>(strlen(block.name())); auto name_length = static_cast<uint16_t>(strlen(block.name()));
auto size = static_cast<uint16_t>(sizeof(BaseBlockData) + name_length + 1); auto size = static_cast<uint16_t>(sizeof(BaseBlockData) + name_length + 1);
#if EASY_MEASURE_STORAGE_EXPAND != 0 #if EASY_MEASURE_STORAGE_EXPAND != 0
const bool expanded = desc->enabled() && blocks.closedList.need_expand(size); const bool expanded = (desc->m_status & profiler::ON) && blocks.closedList.need_expand(size);
profiler::Block b(0ULL, desc->id(), ""); profiler::Block b(0ULL, desc->id(), "");
if (expanded) b.start(); if (expanded) b.start();
#endif #endif
@ -228,7 +225,17 @@ void ThreadStorage::clearClosed()
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
EASY_THREAD_LOCAL static ::ThreadStorage* THREAD_STORAGE = nullptr; ThreadGuard::~ThreadGuard()
{
if (m_id != 0 && THREAD_STORAGE != nullptr && THREAD_STORAGE->id == m_id)
{
//printf("%s Thread expired!\n", THREAD_STORAGE->name.c_str());
THREAD_STORAGE->expired.store(true, std::memory_order_release);
THREAD_STORAGE = nullptr;
}
}
//////////////////////////////////////////////////////////////////////////
ProfileManager::ProfileManager() ProfileManager::ProfileManager()
{ {
@ -239,16 +246,16 @@ ProfileManager::ProfileManager()
ProfileManager::~ProfileManager() ProfileManager::~ProfileManager()
{ {
stopListenSignalToCapture();
stopListenSignalToCapture(); if (m_listenThread.joinable())
if(m_listenThread.joinable()){ m_listenThread.join();
m_listenThread.join();
} for (auto desc : m_descriptors)
for (auto desc : m_descriptors) {
{ if (desc != nullptr)
if (desc != nullptr) delete desc;
delete desc; }
}
} }
ProfileManager& ProfileManager::instance() ProfileManager& ProfileManager::instance()
@ -282,15 +289,21 @@ ThreadStorage* ProfileManager::_findThreadStorage(profiler::thread_id_t _thread_
void ProfileManager::storeBlock(const profiler::BaseBlockDescriptor& _desc, const char* _runtimeName) void ProfileManager::storeBlock(const profiler::BaseBlockDescriptor& _desc, const char* _runtimeName)
{ {
if (!m_isEnabled.load(std::memory_order_acquire) || !_desc.enabled()) if (!m_isEnabled.load(std::memory_order_acquire) || !(_desc.m_status & profiler::ON))
return; return;
profiler::Block b(_desc, _runtimeName);
b.finish(b.begin());
if (THREAD_STORAGE == nullptr) if (THREAD_STORAGE == nullptr)
THREAD_STORAGE = &threadStorage(getCurrentThreadId()); THREAD_STORAGE = &threadStorage(getCurrentThreadId());
#if EASY_ENABLE_BLOCK_STATUS != 0
if (!THREAD_STORAGE->allowChildren)
return;
#endif
profiler::Block b(_desc, _runtimeName);
b.start();
b.m_end = b.m_begin;
THREAD_STORAGE->storeBlock(b); THREAD_STORAGE->storeBlock(b);
} }
@ -302,6 +315,26 @@ void ProfileManager::beginBlock(Block& _block)
if (THREAD_STORAGE == nullptr) if (THREAD_STORAGE == nullptr)
THREAD_STORAGE = &threadStorage(getCurrentThreadId()); THREAD_STORAGE = &threadStorage(getCurrentThreadId());
#if EASY_ENABLE_BLOCK_STATUS != 0
if (THREAD_STORAGE->allowChildren)
{
#endif
if (_block.m_status & profiler::ON)
_block.start();
#if EASY_ENABLE_BLOCK_STATUS != 0
THREAD_STORAGE->allowChildren = !(_block.m_status & profiler::OFF_RECURSIVE);
}
else if (_block.m_status & FORCE_ON_FLAG)
{
_block.start();
_block.m_status = profiler::FORCE_ON_WITHOUT_CHILDREN;
}
else
{
_block.m_status = profiler::OFF_RECURSIVE;
}
#endif
THREAD_STORAGE->blocks.openedList.emplace(_block); THREAD_STORAGE->blocks.openedList.emplace(_block);
} }
@ -334,14 +367,22 @@ void ProfileManager::endBlock()
return; return;
Block& lastBlock = THREAD_STORAGE->blocks.openedList.top(); Block& lastBlock = THREAD_STORAGE->blocks.openedList.top();
if (lastBlock.enabled()) if (lastBlock.m_status & profiler::ON)
{ {
if (!lastBlock.finished()) if (!lastBlock.finished())
lastBlock.finish(); lastBlock.finish();
THREAD_STORAGE->storeBlock(lastBlock); THREAD_STORAGE->storeBlock(lastBlock);
} }
else
{
lastBlock.m_end = lastBlock.m_begin; // this is to restrict endBlock() call inside ~Block()
}
THREAD_STORAGE->blocks.openedList.pop(); THREAD_STORAGE->blocks.openedList.pop();
#if EASY_ENABLE_BLOCK_STATUS != 0
THREAD_STORAGE->allowChildren = THREAD_STORAGE->blocks.openedList.empty() || !(lastBlock.m_status & profiler::OFF_RECURSIVE);
#endif
} }
void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime, bool _lockSpin) void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime, bool _lockSpin)
@ -389,8 +430,7 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
if (wasEnabled) if (wasEnabled)
::profiler::setEnabled(false); ::profiler::setEnabled(false);
//TODO remove it
std::this_thread::sleep_for(std::chrono::milliseconds(20));
// This is to make sure that no new descriptors or new threads will be // This is to make sure that no new descriptors or new threads will be
// added until we finish sending data. // added until we finish sending data.
guard_lock_t lock1(m_storedSpin); guard_lock_t lock1(m_storedSpin);
@ -398,6 +438,12 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
// This is the only place using both spins, so no dead-lock will occur // This is the only place using both spins, so no dead-lock will occur
// Wait for some time to be sure that all operations which began before setEnabled(false) will be finished.
// This is much better than inserting spin-lock or atomic variable check into each store operation.
std::this_thread::sleep_for(std::chrono::milliseconds(20));
// TODO: think about better solution because this one is not 100% safe...
#ifndef _WIN32 #ifndef _WIN32
if (eventTracingEnabled) if (eventTracingEnabled)
{ {
@ -416,15 +462,24 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
} }
#endif #endif
// Calculate used memory total size and total blocks number // Calculate used memory total size and total blocks number
uint64_t usedMemorySize = 0; uint64_t usedMemorySize = 0;
uint32_t blocks_number = 0; uint32_t blocks_number = 0;
for (const auto& thread_storage : m_threads) for (auto it = m_threads.begin(), end = m_threads.end(); it != end;)
{ {
const auto& t = thread_storage.second; const auto& t = it->second;
const uint32_t num = static_cast<uint32_t>(t.blocks.closedList.size()) + static_cast<uint32_t>(t.sync.closedList.size());
if (t.expired.load(std::memory_order_acquire) && num == 0) {
// Thread has been finished and contains no profiled information.
// Remove it now.
m_threads.erase(it++);
continue;
}
usedMemorySize += t.blocks.usedMemorySize + t.sync.usedMemorySize; 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()); blocks_number += num;
++it;
} }
// Write CPU frequency to let GUI calculate real time value from CPU clocks // Write CPU frequency to let GUI calculate real time value from CPU clocks
@ -461,11 +516,11 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
} }
// Write blocks and context switch events for each thread // Write blocks and context switch events for each thread
for (auto& thread_storage : m_threads) for (auto it = m_threads.begin(), end = m_threads.end(); it != end;)
{ {
auto& t = thread_storage.second; auto& t = it->second;
_outputStream.write(thread_storage.first); _outputStream.write(it->first);
const auto name_size = static_cast<uint16_t>(t.name.size() + 1); const auto name_size = static_cast<uint16_t>(t.name.size() + 1);
_outputStream.write(name_size); _outputStream.write(name_size);
@ -482,6 +537,11 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
t.clearClosed(); t.clearClosed();
t.blocks.openedList.clear(); t.blocks.openedList.clear();
t.sync.openedList.clear(); t.sync.openedList.clear();
if (t.expired.load(std::memory_order_acquire))
m_threads.erase(it++); // Remove expired thread after writing all profiled information
else
++it;
} }
// Remove all expired block descriptors (descriptor may become expired if it's .dll/.so have been unloaded during application execution) // Remove all expired block descriptors (descriptor may become expired if it's .dll/.so have been unloaded during application execution)
@ -512,15 +572,22 @@ uint32_t ProfileManager::dumpBlocksToFile(const char* _filename)
return blocksNumber; return blocksNumber;
} }
const char* ProfileManager::registerThread(const char* name) const char* ProfileManager::registerThread(const char* name, ThreadGuard& threadGuard)
{ {
if (THREAD_STORAGE == nullptr) const bool isNewThread = THREAD_STORAGE == nullptr;
if (isNewThread)
{ {
THREAD_STORAGE = &threadStorage(getCurrentThreadId()); const auto id = getCurrentThreadId();
THREAD_STORAGE = &threadStorage(id);
threadGuard.m_id = THREAD_STORAGE->id = id;
} }
if (!THREAD_STORAGE->named) if (!THREAD_STORAGE->named)
{ {
if (!isNewThread) {
threadGuard.m_id = THREAD_STORAGE->id = getCurrentThreadId();
}
THREAD_STORAGE->named = true; THREAD_STORAGE->named = true;
THREAD_STORAGE->name = name; THREAD_STORAGE->name = name;
} }
@ -528,7 +595,7 @@ const char* ProfileManager::registerThread(const char* name)
return THREAD_STORAGE->name.c_str(); return THREAD_STORAGE->name.c_str();
} }
void ProfileManager::setBlockEnabled(profiler::block_id_t _id, const profiler::hashed_stdstring& _key, bool _enabled) void ProfileManager::setBlockStatus(block_id_t _id, const hashed_stdstring& _key, EasyBlockStatus _status)
{ {
guard_lock_t lock(m_storedSpin); guard_lock_t lock(m_storedSpin);
@ -537,16 +604,16 @@ void ProfileManager::setBlockEnabled(profiler::block_id_t _id, const profiler::h
{ {
lock.unlock(); lock.unlock();
*desc->m_pEnable = _enabled; *desc->m_pStatus = _status;
desc->m_enabled = _enabled; // TODO: possible concurrent access, atomic may be needed desc->m_status = _status; // TODO: possible concurrent access, atomic may be needed
} }
else else
{ {
#ifdef _WIN32 #ifdef _WIN32
blocks_enable_status_t::key_type key(_key.c_str(), _key.size(), _key.hcode()); blocks_enable_status_t::key_type key(_key.c_str(), _key.size(), _key.hcode());
m_blocksEnableStatus[key] = _enabled; m_blocksEnableStatus[key] = _status;
#else #else
m_blocksEnableStatus[_key] = _enabled; m_blocksEnableStatus[_key] = _status;
#endif #endif
} }
} }
@ -572,6 +639,7 @@ void ProfileManager::stopListenSignalToCapture()
void ProfileManager::startListen() void ProfileManager::startListen()
{ {
EASY_THREAD("EasyProfiler.Listen");
EasySocket socket; EasySocket socket;
profiler::net::Message replyMessage(profiler::net::MESSAGE_TYPE_REPLY_START_CAPTURING); profiler::net::Message replyMessage(profiler::net::MESSAGE_TYPE_REPLY_START_CAPTURING);
@ -583,9 +651,9 @@ void ProfileManager::startListen()
bool hasConnect = false; bool hasConnect = false;
socket.listen(); socket.listen();
socket.accept(); socket.accept();
EASY_EVENT("ClientConnected", profiler::colors::White, profiler::OFF);
hasConnect = true; hasConnect = true;
printf("Client Accepted!\n"); printf("Client Accepted!\n");
@ -596,7 +664,6 @@ void ProfileManager::startListen()
while (hasConnect && !m_stopListen.load()) while (hasConnect && !m_stopListen.load())
{ {
char buffer[256] = {}; char buffer[256] = {};
bytes = socket.receive(buffer, 255); bytes = socket.receive(buffer, 255);
@ -616,7 +683,8 @@ void ProfileManager::startListen()
case profiler::net::MESSAGE_TYPE_REQUEST_START_CAPTURE: case profiler::net::MESSAGE_TYPE_REQUEST_START_CAPTURE:
{ {
printf("RECEIVED MESSAGE_TYPE_REQUEST_START_CAPTURE\n"); printf("RECEIVED MESSAGE_TYPE_REQUEST_START_CAPTURE\n");
profiler::setEnabled(true); ProfileManager::setEnabled(true);
EASY_EVENT("StartCapture", profiler::colors::Green, profiler::OFF);
replyMessage.type = profiler::net::MESSAGE_TYPE_REPLY_START_CAPTURING; replyMessage.type = profiler::net::MESSAGE_TYPE_REPLY_START_CAPTURING;
bytes = socket.send(&replyMessage, sizeof(replyMessage)); bytes = socket.send(&replyMessage, sizeof(replyMessage));
@ -626,7 +694,8 @@ void ProfileManager::startListen()
case profiler::net::MESSAGE_TYPE_REQUEST_STOP_CAPTURE: case profiler::net::MESSAGE_TYPE_REQUEST_STOP_CAPTURE:
{ {
printf("RECEIVED MESSAGE_TYPE_REQUEST_STOP_CAPTURE\n"); printf("RECEIVED MESSAGE_TYPE_REQUEST_STOP_CAPTURE\n");
profiler::setEnabled(false); EASY_EVENT("StopCapture", profiler::colors::Red, profiler::OFF);
ProfileManager::setEnabled(false);
replyMessage.type = profiler::net::MESSAGE_TYPE_REPLY_PREPARE_BLOCKS; replyMessage.type = profiler::net::MESSAGE_TYPE_REPLY_PREPARE_BLOCKS;
bytes = socket.send(&replyMessage, sizeof(replyMessage)); bytes = socket.send(&replyMessage, sizeof(replyMessage));

View File

@ -20,31 +20,26 @@ along with this program.If not, see <http://www.gnu.org/licenses/>.
#define EASY_PROFILER____MANAGER____H______ #define EASY_PROFILER____MANAGER____H______
#include "profiler/profiler.h" #include "profiler/profiler.h"
#include "profiler/serialized_block.h"
#include "profiler/easy_socket.h" #include "profiler/easy_socket.h"
#include "spin_lock.h" #include "spin_lock.h"
#include "outstream.h" #include "outstream.h"
#include "hashed_cstr.h" #include "hashed_cstr.h"
#include <map> #include <map>
#include <list>
#include <vector> #include <vector>
#include <unordered_map> #include <unordered_map>
#include <string>
#include <functional>
#include <string.h>
#include <thread> #include <thread>
#include <atomic> #include <atomic>
//#include <list>
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
#ifdef _WIN32 #ifdef _WIN32
#include <Windows.h> #include <Windows.h>
#else #else
#include <thread>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <chrono>
#endif #endif
inline uint32_t getCurrentThreadId() inline uint32_t getCurrentThreadId()
@ -58,6 +53,20 @@ inline uint32_t getCurrentThreadId()
#endif #endif
} }
inline profiler::timestamp_t getCurrentTime()
{
#ifdef _WIN32
//see https://msdn.microsoft.com/library/windows/desktop/dn553408(v=vs.85).aspx
LARGE_INTEGER elapsedMicroseconds;
if (!QueryPerformanceCounter(&elapsedMicroseconds))
return 0;
return (profiler::timestamp_t)elapsedMicroseconds.QuadPart;
#else
//std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> time_point;
return std::chrono::time_point_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now()).time_since_epoch().count();
#endif
}
namespace profiler { namespace profiler {
class SerializedBlock; class SerializedBlock;
@ -72,9 +81,10 @@ namespace profiler {
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
//#define EASY_ENABLE_ALIGNMENT #define EASY_ENABLE_BLOCK_STATUS 1
#define EASY_ENABLE_ALIGNMENT 0
#ifndef EASY_ENABLE_ALIGNMENT #if EASY_ENABLE_ALIGNMENT == 0
# define EASY_ALIGNED(TYPE, VAR, A) TYPE VAR # define EASY_ALIGNED(TYPE, VAR, A) TYPE VAR
# define EASY_MALLOC(MEMSIZE, A) malloc(MEMSIZE) # define EASY_MALLOC(MEMSIZE, A) malloc(MEMSIZE)
# define EASY_FREE(MEMPTR) free(MEMPTR) # define EASY_FREE(MEMPTR) free(MEMPTR)
@ -288,20 +298,24 @@ struct BlocksList final
}; };
class ThreadStorage final struct ThreadStorage final
{ {
public:
BlocksList<std::reference_wrapper<profiler::Block>, SIZEOF_CSWITCH * (uint16_t)128U> blocks; BlocksList<std::reference_wrapper<profiler::Block>, SIZEOF_CSWITCH * (uint16_t)128U> blocks;
BlocksList<profiler::Block, SIZEOF_CSWITCH * (uint16_t)128U> sync; BlocksList<profiler::Block, SIZEOF_CSWITCH * (uint16_t)128U> sync;
std::string name; std::string name;
profiler::thread_id_t id = 0;
std::atomic_bool expired;
bool allowChildren = true;
bool named = false; bool named = false;
void storeBlock(const profiler::Block& _block); void storeBlock(const profiler::Block& _block);
void storeCSwitch(const profiler::Block& _block); void storeCSwitch(const profiler::Block& _block);
void clearClosed(); void clearClosed();
ThreadStorage() = default; ThreadStorage()
{
expired = ATOMIC_VAR_INIT(false);
}
}; };
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -319,9 +333,9 @@ class ProfileManager final
typedef std::vector<profiler::BlockDescriptor*> block_descriptors_t; typedef std::vector<profiler::BlockDescriptor*> block_descriptors_t;
#ifdef _WIN32 #ifdef _WIN32
typedef std::unordered_map<profiler::hashed_cstr, bool> blocks_enable_status_t; typedef std::unordered_map<profiler::hashed_cstr, profiler::EasyBlockStatus> blocks_enable_status_t;
#else #else
typedef std::unordered_map<profiler::hashed_stdstring, bool> blocks_enable_status_t; typedef std::unordered_map<profiler::hashed_stdstring, profiler::EasyBlockStatus> blocks_enable_status_t;
#endif #endif
map_of_threads_stacks m_threads; map_of_threads_stacks m_threads;
@ -339,7 +353,7 @@ class ProfileManager final
#endif #endif
uint32_t dumpBlocksToStream(profiler::OStream& _outputStream); uint32_t dumpBlocksToStream(profiler::OStream& _outputStream);
void setBlockEnabled(profiler::block_id_t _id, const profiler::hashed_stdstring& _key, bool _enabled); void setBlockStatus(profiler::block_id_t _id, const profiler::hashed_stdstring& _key, profiler::EasyBlockStatus _status);
std::thread m_listenThread; std::thread m_listenThread;
bool m_isAlreadyListened = false; bool m_isAlreadyListened = false;
@ -354,9 +368,9 @@ public:
~ProfileManager(); ~ProfileManager();
template <class ... TArgs> template <class ... TArgs>
const profiler::BaseBlockDescriptor* addBlockDescriptor(bool _enabledByDefault, const char* _autogenUniqueId, TArgs ... _args) const profiler::BaseBlockDescriptor* addBlockDescriptor(profiler::EasyBlockStatus _defaultStatus, const char* _autogenUniqueId, TArgs ... _args)
{ {
auto desc = new profiler::BlockDescriptor(_enabledByDefault, _args...); auto desc = new profiler::BlockDescriptor(_defaultStatus, _args...);
guard_lock_t lock(m_storedSpin); guard_lock_t lock(m_storedSpin);
m_usedMemorySize += desc->m_size; m_usedMemorySize += desc->m_size;
@ -367,12 +381,12 @@ public:
auto it = m_blocksEnableStatus.find(key); auto it = m_blocksEnableStatus.find(key);
if (it != m_blocksEnableStatus.end()) if (it != m_blocksEnableStatus.end())
{ {
desc->m_enabled = it->second; desc->m_status = it->second;
desc->m_pEnable = &it->second; desc->m_pStatus = &it->second;
} }
else else
{ {
desc->m_pEnable = &m_blocksEnableStatus.emplace(key, desc->enabled()).first->second; desc->m_pStatus = &m_blocksEnableStatus.emplace(key, desc->status()).first->second;
} }
return desc; return desc;
@ -384,7 +398,7 @@ public:
void setEnabled(bool isEnable); void setEnabled(bool isEnable);
void setEventTracingEnabled(bool _isEnable); void setEventTracingEnabled(bool _isEnable);
uint32_t dumpBlocksToFile(const char* filename); uint32_t dumpBlocksToFile(const char* filename);
const char* registerThread(const char* name);// , const char* filename, const char* _funcname, int line); const char* registerThread(const char* name, profiler::ThreadGuard& threadGuard);
#ifndef _WIN32 #ifndef _WIN32
void setContextSwitchLogFilename(const char* name) void setContextSwitchLogFilename(const char* name)