0
0
mirror of https://github.com/yse/easy_profiler.git synced 2024-12-26 08:01:51 +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 ...
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){
EASY_BLOCK("Calling bar()"); // Block with default color
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_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...
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
@ -83,7 +88,7 @@ Block will be automatically completed by destructor.
}
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...
}
\endcode
@ -158,8 +163,10 @@ will end previously opened EASY_BLOCK or EASY_FUNCTION.
*/
# define EASY_THREAD(name)\
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)\
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.
@ -295,7 +302,7 @@ Otherwise, no log messages will be printed.
//////////////////////////////////////////////////////////////////////////
class ProfileManager;
class ThreadStorage;
struct ThreadStorage;
namespace profiler {
@ -323,16 +330,17 @@ namespace profiler {
class PROFILER_API BaseBlockDescriptor
{
friend ::ProfileManager;
friend ::ThreadStorage;
protected:
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
color_t m_color; ///< Color of the block packed into 1-byte structure
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
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
color_t m_color; ///< Color of the block packed into 1-byte structure
block_type_t m_type; ///< Type of the block (See BlockType)
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:
@ -340,7 +348,7 @@ namespace profiler {
inline int line() const { return m_line; }
inline color_t color() const { return m_color; }
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.
@ -381,17 +389,17 @@ namespace profiler {
{
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_filename; ///< Source file name where this block is declared
bool* m_pEnable; ///< Pointer to the enable flag in unordered_map
uint16_t m_size; ///< Used memory size
bool m_expired; ///< Is this descriptor expired
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
EasyBlockStatus* m_pStatus; ///< Pointer to the enable flag in unordered_map
uint16_t m_size; ///< Used memory size
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:
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 {
return m_name;
@ -410,8 +418,8 @@ namespace profiler {
friend ::ProfileManager;
friend ::ThreadStorage;
const char* m_name;
bool m_enabled;
const char* m_name;
EasyBlockStatus m_status;
private:
@ -420,7 +428,8 @@ namespace profiler {
void finish();
void finish(timestamp_t _time);
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:
@ -459,6 +468,13 @@ namespace profiler {
}; // END of class BlockDescRef.
class PROFILER_API ThreadGuard final {
friend ::ProfileManager;
thread_id_t m_id = 0;
public:
~ThreadGuard();
};
//////////////////////////////////////////////////////////////////////
// Core API
// Note: it is better to use macros defined above than a direct calls to API.
@ -472,7 +488,7 @@ namespace 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.
@ -517,7 +533,7 @@ namespace 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.
@ -555,15 +571,11 @@ namespace profiler {
PROFILER_API const char* getContextSwitchLogFilename();
#endif
PROFILER_API void startListenSignalToCapture();
PROFILER_API void stopListenSignalToCapture();
PROFILER_API void startListenSignalToCapture();
PROFILER_API void stopListenSignalToCapture();
}
inline void setEnabled(::profiler::EasyEnableFlag _isEnable) {
setEnabled(_isEnable == ::profiler::ENABLED);
}
//////////////////////////////////////////////////////////////////////
} // END of namespace profiler.

View File

@ -80,10 +80,13 @@
namespace profiler {
enum EasyEnableFlag : uint8_t
{
DISABLED = 0,
ENABLED = 1
enum EasyBlockStatus : uint8_t {
OFF = 0, ///< The block is OFF
ON = 1, ///< The block is ON (but if it's parent block is off recursively then this block will be off too)
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 {
@ -126,7 +129,7 @@ namespace profiler {
}
template <class ... TArgs>
inline color_t extract_color(::profiler::EasyEnableFlag, TArgs...) {
inline color_t extract_color(::profiler::EasyBlockStatus, TArgs...) {
return ::profiler::colors::Default;
}
@ -148,24 +151,24 @@ namespace profiler {
//***********************************************
inline bool extract_enable_flag() {
return true;
inline EasyBlockStatus extract_enable_flag() {
return ::profiler::ON;
}
template <class T, class ... TArgs>
inline bool extract_enable_flag(T, ::profiler::EasyEnableFlag _flag, TArgs...) {
return _flag == ::profiler::ENABLED;
inline EasyBlockStatus extract_enable_flag(T, ::profiler::EasyBlockStatus _flag, TArgs...) {
return _flag;
}
template <class ... TArgs>
inline bool extract_enable_flag(::profiler::EasyEnableFlag _flag, TArgs...) {
return _flag == ::profiler::ENABLED;
inline EasyBlockStatus extract_enable_flag(::profiler::EasyBlockStatus _flag, TArgs...) {
return _flag;
}
template <class ... TArgs>
inline bool extract_enable_flag(TArgs...) {
static_assert(sizeof...(TArgs) < 2, "No EasyEnableFlag in arguments list for EASY_BLOCK(name, ...)!");
return true;
inline EasyBlockStatus extract_enable_flag(TArgs...) {
static_assert(sizeof...(TArgs) < 2, "No EasyBlockStatus in arguments list for EASY_BLOCK(name, ...)!");
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"
class ThreadStorage;
namespace profiler {
//////////////////////////////////////////////////////////////////////////
@ -69,9 +67,9 @@ namespace profiler {
return name() + m_nameLength;
}
inline void setEnabled(bool _enabled)
inline void setStatus(EasyBlockStatus _status)
{
m_enabled = _enabled;
m_status = _status;
}
private:

View File

@ -38,6 +38,7 @@
************************************************************************/
#include <QMenu>
#include <QAction>
#include <QHeaderView>
#include <QContextMenuEvent>
#include <QSignalBlocker>
@ -442,18 +443,24 @@ void EasyTreeWidget::contextMenuEvent(QContextMenuEvent* _event)
}
}
auto id = item->block().node->id();
action = menu.addAction("Block enabled");
action->setCheckable(true);
action->setChecked(easyDescriptor(id).enabled());
action->setData(id);
connect(action, &QAction::triggered, this, &This::onBlockEnableDisable);
const auto& desc = easyDescriptor(item->block().node->id());
auto submenu = menu.addMenu("Block status");
if (action->isChecked()) {
auto f = action->font();
f.setBold(true);
action->setFont(f);
}
#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.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());
if (action != nullptr)
{
auto id = action->data().toUInt();
easyDescriptor(id).setEnabled(_checked);
emit EASY_GLOBALS.events.enableStatusChanged(id, _checked);
auto& desc = easyDescriptor(item->block().node->id());
desc.setStatus(static_cast<::profiler::EasyBlockStatus>(action->data().toUInt()));
emit EASY_GLOBALS.events.blockStatusChanged(desc.id(), desc.status());
}
}

View File

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

View File

@ -31,6 +31,7 @@
#include <QMenu>
#include <QAction>
#include <QActionGroup>
#include <QHeaderView>
#include <QString>
#include <QContextMenuEvent>
@ -63,14 +64,92 @@
enum DescColumns
{
DESC_COL_FILE_LINE = 0,
DESC_COL_TYPE,
DESC_COL_NAME,
DESC_COL_STATUS,
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();
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_STATUS, "Status");
setHeaderItem(header_item);
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::itemDoubleClicked, this, &This::onDoubleClick);
connect(this, &Parent::currentItemChanged, this, &This::onCurrentItemChange);
@ -155,12 +235,40 @@ void EasyDescTreeWidget::contextMenuEvent(QContextMenuEvent* _event)
auto header_item = headerItem();
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);
if (i == m_searchColumn)
action->setChecked(true);
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());
@ -254,6 +362,8 @@ void EasyDescTreeWidget::build()
{
p.item = new QTreeWidgetItem();
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());
@ -264,21 +374,20 @@ void EasyDescTreeWidget::build()
item->setData(DESC_COL_FILE_LINE, Qt::UserRole, desc->line());
item->setText(DESC_COL_NAME, desc->name());
item->setFont(DESC_COL_STATUS, f);
QBrush brush;
if (desc->enabled())
if (desc->type() == ::profiler::BLOCK_TYPE_BLOCK)
{
item->setText(DESC_COL_STATUS, "ON");
brush.setColor(QColor::fromRgba(ENABLED_COLOR));
item->setText(DESC_COL_TYPE, "B");
item->setToolTip(DESC_COL_TYPE, "Block");
}
else
{
item->setText(DESC_COL_STATUS, "OFF");
brush.setColor(QColor::fromRgba(DISABLED_COLOR));
item->setText(DESC_COL_TYPE, "E");
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;
}
@ -312,29 +421,17 @@ void EasyDescTreeWidget::onItemExpand(QTreeWidgetItem*)
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& desc = easyDescriptor(item->desc());
desc.setStatus(nextStatus(desc.status()));
QBrush brush;
if (desc.enabled())
{
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);
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.enableStatusChanged(item->desc(), desc.enabled());
emit EASY_GLOBALS.events.blockStatusChanged(desc.id(), desc.status());
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)
return;
@ -370,19 +490,9 @@ void EasyDescTreeWidget::onEnableStatusChange(::profiler::block_id_t _id, bool _
if (item == nullptr)
return;
QBrush brush;
if (_enabled)
{
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);
auto& desc = easyDescriptor(item->desc());
item->setText(DESC_COL_STATUS, statusText(desc.status()));
item->setForeground(DESC_COL_STATUS, QColor::fromRgba(statusColor(desc.status())));
}
//////////////////////////////////////////////////////////////////////////

View File

@ -107,11 +107,12 @@ public slots:
private slots:
void onSearchColumnChange(bool);
void onBlockStatusChangeClicked(bool);
void onCurrentItemChange(QTreeWidgetItem* _item, QTreeWidgetItem* _prev);
void onItemExpand(QTreeWidgetItem* _item);
void onDoubleClick(QTreeWidgetItem* _item, int _column);
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();
private:

View File

@ -52,7 +52,7 @@ namespace profiler_gui {
void itemsExpandStateChanged();
void drawBordersChanged();
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.

View File

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

View File

@ -23,35 +23,11 @@
* : You should have received a copy of the GNU General Public License
* : along with this program.If not, see <http://www.gnu.org/licenses/>.
************************************************************************/
#include "profiler/profiler.h"
#include "profile_manager.h"
#include <ctime>
#include <chrono>
#include <thread>
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)
: m_begin(_begin_time)
, m_end(0)
@ -63,23 +39,25 @@ BaseBlockData::BaseBlockData(timestamp_t _begin_time, block_id_t _descriptor_id)
Block::Block(Block&& that)
: BaseBlockData(that.m_begin, that.m_id)
, m_name(that.m_name)
, m_enabled(that.m_enabled)
, m_status(that.m_status)
{
m_end = that.m_end;
}
Block::Block(const BaseBlockDescriptor& _descriptor, const char* _runtimeName)
: BaseBlockData(_descriptor.enabled() ? getCurrentTime() : 1ULL, _descriptor.id())
: BaseBlockData(1ULL, _descriptor.id())
, 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)
: BaseBlockData(_begin_time, _descriptor_id)
, m_name(_runtimeName)
, m_enabled(true)
, m_status(::profiler::ON)
{
}
void Block::start()

View File

@ -103,7 +103,7 @@ namespace profiler {
if (sizeof(CSwitch) != _traceEvent->UserDataLength)
return;
EASY_FUNCTION(::profiler::colors::White, ::profiler::DISABLED);
EASY_FUNCTION(::profiler::colors::White, ::profiler::OFF);
auto _contextSwitchEvent = reinterpret_cast<CSwitch*>(_traceEvent->UserData);
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
* : along with this program.If not, see <http://www.gnu.org/licenses/>.
************************************************************************/
#include <algorithm>
#include <fstream>
#include "profile_manager.h"
#include "profiler/serialized_block.h"
#include "profiler/easy_net.h"
#include "profiler/easy_socket.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;
#ifdef _WIN32
extern decltype(LARGE_INTEGER::QuadPart) CPU_FREQUENCY;
#endif
extern timestamp_t getCurrentTime();
const uint8_t FORCE_ON_FLAG = profiler::FORCE_ON & ~profiler::ON;
//auto& 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" {
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()
@ -85,9 +82,9 @@ extern "C" {
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)
@ -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_line(_line)
, m_type(_block_type)
, 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)
: BaseBlockDescriptor(_id, _enabled, _line, _block_type, _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, _status, _line, _block_type, _color)
, m_name(_name)
, m_filename(_filename)
, m_pEnable(nullptr)
, m_pStatus(nullptr)
, m_size(static_cast<uint16_t>(sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2))
, m_expired(false)
{
}
BlockDescriptor::BlockDescriptor(bool _enabled, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color)
: BaseBlockDescriptor(0, _enabled, _line, _block_type, _color)
BlockDescriptor::BlockDescriptor(EasyBlockStatus _status, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color)
: BaseBlockDescriptor(0, _status, _line, _block_type, _color)
, m_name(_name)
, m_filename(_filename)
, m_pEnable(nullptr)
, m_pStatus(nullptr)
, m_size(static_cast<uint16_t>(sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2))
, m_expired(false)
{
@ -178,14 +175,14 @@ 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, 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
auto name_length = static_cast<uint16_t>(strlen(block.name()));
auto size = static_cast<uint16_t>(sizeof(BaseBlockData) + name_length + 1);
#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(), "");
if (expanded) b.start();
#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()
{
@ -239,16 +246,16 @@ ProfileManager::ProfileManager()
ProfileManager::~ProfileManager()
{
stopListenSignalToCapture();
if(m_listenThread.joinable()){
m_listenThread.join();
}
for (auto desc : m_descriptors)
{
if (desc != nullptr)
delete desc;
}
stopListenSignalToCapture();
if (m_listenThread.joinable())
m_listenThread.join();
for (auto desc : m_descriptors)
{
if (desc != nullptr)
delete desc;
}
}
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)
{
if (!m_isEnabled.load(std::memory_order_acquire) || !_desc.enabled())
if (!m_isEnabled.load(std::memory_order_acquire) || !(_desc.m_status & profiler::ON))
return;
profiler::Block b(_desc, _runtimeName);
b.finish(b.begin());
if (THREAD_STORAGE == nullptr)
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);
}
@ -302,6 +315,26 @@ void ProfileManager::beginBlock(Block& _block)
if (THREAD_STORAGE == nullptr)
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);
}
@ -334,14 +367,22 @@ void ProfileManager::endBlock()
return;
Block& lastBlock = THREAD_STORAGE->blocks.openedList.top();
if (lastBlock.enabled())
if (lastBlock.m_status & profiler::ON)
{
if (!lastBlock.finished())
lastBlock.finish();
THREAD_STORAGE->storeBlock(lastBlock);
}
else
{
lastBlock.m_end = lastBlock.m_begin; // this is to restrict endBlock() call inside ~Block()
}
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)
@ -389,8 +430,7 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
if (wasEnabled)
::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
// added until we finish sending data.
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
// 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
if (eventTracingEnabled)
{
@ -416,15 +462,24 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
}
#endif
// Calculate used memory total size and total blocks number
uint64_t usedMemorySize = 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;
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
@ -461,11 +516,11 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
}
// 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);
_outputStream.write(name_size);
@ -482,6 +537,11 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
t.clearClosed();
t.blocks.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)
@ -512,15 +572,22 @@ uint32_t ProfileManager::dumpBlocksToFile(const char* _filename)
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 (!isNewThread) {
threadGuard.m_id = THREAD_STORAGE->id = getCurrentThreadId();
}
THREAD_STORAGE->named = true;
THREAD_STORAGE->name = name;
}
@ -528,7 +595,7 @@ const char* ProfileManager::registerThread(const char* name)
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);
@ -537,16 +604,16 @@ void ProfileManager::setBlockEnabled(profiler::block_id_t _id, const profiler::h
{
lock.unlock();
*desc->m_pEnable = _enabled;
desc->m_enabled = _enabled; // TODO: possible concurrent access, atomic may be needed
*desc->m_pStatus = _status;
desc->m_status = _status; // TODO: possible concurrent access, atomic may be needed
}
else
{
#ifdef _WIN32
blocks_enable_status_t::key_type key(_key.c_str(), _key.size(), _key.hcode());
m_blocksEnableStatus[key] = _enabled;
m_blocksEnableStatus[key] = _status;
#else
m_blocksEnableStatus[_key] = _enabled;
m_blocksEnableStatus[_key] = _status;
#endif
}
}
@ -572,6 +639,7 @@ void ProfileManager::stopListenSignalToCapture()
void ProfileManager::startListen()
{
EASY_THREAD("EasyProfiler.Listen");
EasySocket socket;
profiler::net::Message replyMessage(profiler::net::MESSAGE_TYPE_REPLY_START_CAPTURING);
@ -583,9 +651,9 @@ void ProfileManager::startListen()
bool hasConnect = false;
socket.listen();
socket.accept();
EASY_EVENT("ClientConnected", profiler::colors::White, profiler::OFF);
hasConnect = true;
printf("Client Accepted!\n");
@ -596,7 +664,6 @@ void ProfileManager::startListen()
while (hasConnect && !m_stopListen.load())
{
char buffer[256] = {};
bytes = socket.receive(buffer, 255);
@ -616,7 +683,8 @@ void ProfileManager::startListen()
case profiler::net::MESSAGE_TYPE_REQUEST_START_CAPTURE:
{
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;
bytes = socket.send(&replyMessage, sizeof(replyMessage));
@ -626,7 +694,8 @@ void ProfileManager::startListen()
case profiler::net::MESSAGE_TYPE_REQUEST_STOP_CAPTURE:
{
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;
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______
#include "profiler/profiler.h"
#include "profiler/serialized_block.h"
#include "profiler/easy_socket.h"
#include "spin_lock.h"
#include "outstream.h"
#include "hashed_cstr.h"
#include <map>
#include <list>
#include <vector>
#include <unordered_map>
#include <string>
#include <functional>
#include <string.h>
#include <thread>
#include <atomic>
//#include <list>
//////////////////////////////////////////////////////////////////////////
#ifdef _WIN32
#include <Windows.h>
#else
#include <thread>
#include <sys/types.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <chrono>
#endif
inline uint32_t getCurrentThreadId()
@ -58,6 +53,20 @@ inline uint32_t getCurrentThreadId()
#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 {
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_MALLOC(MEMSIZE, A) malloc(MEMSIZE)
# 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<profiler::Block, SIZEOF_CSWITCH * (uint16_t)128U> sync;
std::string name;
profiler::thread_id_t id = 0;
std::atomic_bool expired;
bool allowChildren = true;
bool named = false;
void storeBlock(const profiler::Block& _block);
void storeCSwitch(const profiler::Block& _block);
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;
#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
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
map_of_threads_stacks m_threads;
@ -339,7 +353,7 @@ class ProfileManager final
#endif
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;
bool m_isAlreadyListened = false;
@ -354,9 +368,9 @@ public:
~ProfileManager();
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);
m_usedMemorySize += desc->m_size;
@ -367,12 +381,12 @@ public:
auto it = m_blocksEnableStatus.find(key);
if (it != m_blocksEnableStatus.end())
{
desc->m_enabled = it->second;
desc->m_pEnable = &it->second;
desc->m_status = it->second;
desc->m_pStatus = &it->second;
}
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;
@ -384,7 +398,7 @@ public:
void setEnabled(bool isEnable);
void setEventTracingEnabled(bool _isEnable);
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
void setContextSwitchLogFilename(const char* name)