From 40a8ee3d16fae3ebf3c76093cee08bb6557b331f Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Wed, 7 Sep 2016 22:37:05 +0300 Subject: [PATCH 01/25] Material design colors --- include/profiler/profiler.h | 2 +- include/profiler/profiler_colors.h | 87 ++++++------------------------ profiler_gui/common_types.h | 14 +++-- sample/main.cpp | 87 ++++++------------------------ 4 files changed, 40 insertions(+), 150 deletions(-) diff --git a/include/profiler/profiler.h b/include/profiler/profiler.h index 038e9c8..b9da0d5 100644 --- a/include/profiler/profiler.h +++ b/include/profiler/profiler.h @@ -251,7 +251,7 @@ namespace profiler { typedef BlockType block_type_t; extern "C" { - PROFILER_API block_id_t registerDescription(const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color = DefaultBlockColor); + PROFILER_API block_id_t registerDescription(const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color = ::profiler::colors::Default); PROFILER_API void beginBlock(Block& _block); PROFILER_API void endBlock(); PROFILER_API void setEnabled(bool isEnable); diff --git a/include/profiler/profiler_colors.h b/include/profiler/profiler_colors.h index 8b69439..8107305 100644 --- a/include/profiler/profiler_colors.h +++ b/include/profiler/profiler_colors.h @@ -26,77 +26,24 @@ along with this program.If not, see . namespace profiler { - ////////////////////////////////////////////////////////////////////// - typedef uint32_t color_t; // Standard four-byte ARGB color format - //typedef uint8_t color_t; // One-byte RGB color format: RRR-GGG-BB - //typedef uint32_t rgb32_t; // Standard four-byte ARGB color format - - ////////////////////////////////////////////////////////////////////// - namespace colors { -// ///< Extracts [0 .. 224] Red value from one-byte RGB format. Possible values are: [0x0, 0x20, 0x40, 0x60, 0x80, 0xa0, 0xe0]. -// inline rgb32_t get_red(color_t color) { return color & 0xe0; } -// -// ///< Extracts [0 .. 224] Green value from one-byte RGB format. Possible values are: [0x0, 0x20, 0x40, 0x60, 0x80, 0xa0, 0xe0]. -// inline rgb32_t get_green(color_t color) { return (color & 0x1c) << 3; } -// -// ///< Extracts [0 .. 192] Blue value from one-byte RGB format. Possible values are: [0x0, 0x40, 0x80, 0xc0] -// inline rgb32_t get_blue(color_t color) { return (color & 3) << 6; } -// -// -// ///< Extracts [0 .. 255] Red value from four-byte RGB format. -// inline rgb32_t rgb_red(rgb32_t color) { return (color & 0x00ff0000) >> 16; } -// -// ///< Extracts [0 .. 255] Green value from four-byte RGB format. -// inline rgb32_t rgb_green(rgb32_t color) { return (color & 0x0000ff00) >> 8; } -// -// ///< Extracts [0 .. 255] Blue value from four-byte RGB format. -// inline rgb32_t rgb_blue(rgb32_t color) { return color & 0x000000ff; } -// -// ///< Unpacks one-byte RGB value into standard four-byte RGB value. -// inline rgb32_t convert_to_rgb(color_t color) { return (get_red(color) << 16) | ((color & 0x1c) << 11) | get_blue(color); } -// -// ///< Packs standard four-byte RGB value into one-byte RGB value. R & G values packed with 0x20 (32) step, B value is packed with 0x40 (64) step. -// inline color_t from_rgb(rgb32_t color) { return (rgb_red(color) & 0xe0) | (((color & 0x0000ff00) >> 11) & 0x1c) | (rgb_blue(color) >> 6); } -// -// ///< Packs standard four-byte RGB value into one-byte RGB value. R & G values packed with 0x20 (32) step, B value is packed with 0x40 (64) step. -// inline color_t from_rgb(color_t red, color_t green, color_t blue) { return (red & 0xe0) | ((green >> 3) & 0x1c) | (blue >> 6); } + ///< Change alpha for color. Only 8 major bytes (0xff000000) used from alpha. + inline color_t modify_alpha32(color_t _color, color_t _alpha) { + return (_alpha & 0xff000000) | (_color & 0x00ffffff); + } + ///< Change alpha for color. + inline color_t modify_alpha8(color_t _color, uint8_t _alpha) { + return (static_cast(_alpha) << 24) | (_color & 0x00ffffff); + } -// const color_t Black = 0x00; // 0x00000000 -// const color_t Random = Black; // Black // Currently GUI interprets Black color as permission to select random color for block -// const color_t Lightgray = 0x6E; // 0x00606080 -// const color_t Darkgray = 0x25; // 0x00202040 -// const color_t White = 0xFF; // 0x00E0E0C0 -// const color_t Red = 0xE0; // 0x00E00000 -// const color_t Green = 0x1C; // 0x0000E000 -// const color_t Blue = 0x03; // 0x000000C0 -// const color_t Magenta = (Red | Blue); // 0x00E000C0 -// const color_t Cyan = (Green | Blue); // 0x0000E0C0 -// const color_t Yellow = (Red | Green); // 0x00E0E000 -// const color_t Darkred = 0x60; // 0x00600000 -// const color_t Darkgreen = 0x0C; // 0x00006000 -// const color_t Darkblue = 0x01; // 0x00000040 -// const color_t Darkmagenta = (Darkred | Darkblue); // 0x00600040 -// const color_t Darkcyan = (Darkgreen | Darkblue); // 0x00006040 -// const color_t Darkyellow = (Darkred | Darkgreen); // 0x00606000 -// const color_t Navy = 0x02; // 0x00000080 -// const color_t Teal = 0x12; // 0x00008080 -// const color_t Maroon = 0x80; // 0x00800000 -// const color_t Purple = 0x82; // 0x00800080 -// const color_t Olive = 0x90; // 0x00808000 -// const color_t Grey = 0x92; // 0x00808080 -// const color_t Silver = 0xDB; // 0x00C0C0C0 -// const color_t Orange = 0xF4; // 0x00E0A000 -// const color_t Coral = 0xF6; // 0x00E0A080 -// const color_t Brick = 0xED; // 0x00E06040 -// const color_t Clay = 0xD6; // 0x00C0A080 -// const color_t Skin = 0xFA; // 0x00E0C080 -// const color_t Palegold = 0xFE; // 0x00E0E080 - + ///< Create color from ARGB components. + inline color_t color(uint8_t _red, uint8_t _green, uint8_t _blue, uint8_t _alpha = 0xff) { + return (static_cast(_alpha) << 24) | (static_cast(_red) << 16) | (static_cast(_green) << 8) | static_cast(_blue); + } // Google Material Design colors @@ -420,17 +367,17 @@ namespace profiler { const color_t Coral = DeepOrange200; const color_t Brown = Brown500; const color_t DarkBrown = Brown900; - const color_t CreamWhite = Brown50; + const color_t CreamWhite = Orange50; + const color_t Wheat = Amber100; const color_t Grey = Grey500; + const color_t Dark = Grey900; const color_t Silver = Grey300; const color_t BlueGrey = BlueGrey500; + const color_t Default = Wheat; + } // END of namespace colors. - const color_t DefaultBlockColor = colors::OrangeA100; - - ////////////////////////////////////////////////////////////////////// - } // END of namespace profiler. ////////////////////////////////////////////////////////////////////// diff --git a/profiler_gui/common_types.h b/profiler_gui/common_types.h index 7a195c6..ab84095 100644 --- a/profiler_gui/common_types.h +++ b/profiler_gui/common_types.h @@ -101,24 +101,22 @@ struct do_no_hash { ////////////////////////////////////////////////////////////////////////// -const QRgb DEFAULT_COLOR = profiler::DefaultBlockColor;// 0x00d4b494; - -inline QRgb toRgb(unsigned int _red, unsigned int _green, unsigned int _blue) +inline QRgb toRgb(uint32_t _red, uint32_t _green, uint32_t _blue) { return (_red << 16) + (_green << 8) + _blue; } -inline QRgb fromProfilerRgb(unsigned int _red, unsigned int _green, unsigned int _blue) +inline QRgb fromProfilerRgb(uint32_t _red, uint32_t _green, uint32_t _blue) { if (_red == 0 && _green == 0 && _blue == 0) - return DEFAULT_COLOR; + return ::profiler::colors::Default; return toRgb(_red, _green, _blue) | 0x00141414; } -inline QRgb textColorForRgb(QRgb _color) +inline ::profiler::color_t textColorForRgb(::profiler::color_t _color) { - const QRgb sum = 0xff - ((_color & 0xff000000) >> 24) + ((_color & 0x00ff0000) >> 16) + ((_color & 0x0000ff00) >> 8) + (_color & 0x000000ff); - return sum > 0x215 ? ::profiler::colors::Black : ::profiler::colors::White; + const auto sum = 255. - (((_color & 0x00ff0000) >> 16) * 0.299 + ((_color & 0x0000ff00) >> 8) * 0.587 + (_color & 0x000000ff) * 0.114); + return sum < 76.5 || ((_color & 0xff000000) >> 24) < 0x80 ? ::profiler::colors::Dark : ::profiler::colors::CreamWhite; } ////////////////////////////////////////////////////////////////////////// diff --git a/sample/main.cpp b/sample/main.cpp index 1773295..350ac99 100644 --- a/sample/main.cpp +++ b/sample/main.cpp @@ -14,7 +14,7 @@ std::mutex cv_m; int g_i = 0; int OBJECTS = 500; -int RENDER_SPEPS = 1600; +int RENDER_STEPS = 1600; int MODELLING_STEPS = 1000; int RESOURCE_LOADING_COUNT = 50; @@ -32,7 +32,7 @@ void loadingResources(){ } void prepareMath(){ - EASY_FUNCTION(profiler::colors::Blue); + EASY_FUNCTION(profiler::colors::Green); int* intarray = new int[OBJECTS]; for (int i = 0; i < OBJECTS; ++i) intarray[i] = i * i; @@ -41,7 +41,7 @@ void prepareMath(){ } void calcIntersect(){ - EASY_FUNCTION(profiler::colors::Blue); + EASY_FUNCTION(profiler::colors::Gold); //int* intarray = new int[OBJECTS * OBJECTS]; int* intarray = new int[OBJECTS]; for (int i = 0; i < OBJECTS; ++i) @@ -61,7 +61,7 @@ double multModel(double i) } void calcPhys(){ - EASY_FUNCTION(profiler::colors::Blue); + EASY_FUNCTION(profiler::colors::Amber); double* intarray = new double[OBJECTS]; for (int i = 0; i < OBJECTS; ++i) intarray[i] = multModel(double(i)) + double(i / 3) - double((OBJECTS - i) / 2); @@ -71,12 +71,12 @@ void calcPhys(){ double calcSubbrain(int i) { - EASY_FUNCTION(profiler::colors::Blue); + EASY_FUNCTION(profiler::colors::Navy); return i * i * i - i / 10 + (OBJECTS - i) * 7 ; } void calcBrain(){ - EASY_FUNCTION(profiler::colors::Blue); + EASY_FUNCTION(profiler::colors::LightBlue); double* intarray = new double[OBJECTS]; for (int i = 0; i < OBJECTS; ++i) intarray[i] = calcSubbrain(i) + double(i * 180 / 3); @@ -85,19 +85,19 @@ void calcBrain(){ } void calculateBehavior(){ - EASY_FUNCTION(profiler::colors::DarkBlue); + EASY_FUNCTION(profiler::colors::Blue); calcPhys(); calcBrain(); } void modellingStep(){ - EASY_FUNCTION(profiler::colors::Navy); + EASY_FUNCTION(); prepareMath(); calculateBehavior(); } void prepareRender(){ - EASY_FUNCTION(profiler::colors::DarkRed); + EASY_FUNCTION(profiler::colors::Brick); localSleep(); //std::this_thread::sleep_for(std::chrono::milliseconds(8)); @@ -105,7 +105,7 @@ void prepareRender(){ int multPhys(int i) { - EASY_FUNCTION(profiler::colors::Red); + EASY_FUNCTION(profiler::colors::Red700); return i * i * i * i / 100; } @@ -146,7 +146,7 @@ void modellingThread(){ //std::unique_lock lk(cv_m); //cv.wait(lk, []{return g_i == 1; }); EASY_THREAD("Modelling"); - for (int i = 0; i < RENDER_SPEPS; i++){ + for (int i = 0; i < RENDER_STEPS; i++){ modellingStep(); localSleep(1200000); //std::this_thread::sleep_for(std::chrono::milliseconds(20)); @@ -164,60 +164,7 @@ void renderThread(){ } } -void four() -{ - EASY_FUNCTION(profiler::colors::Red); - std::this_thread::sleep_for(std::chrono::milliseconds(37)); -} - -void five() -{ - EASY_FUNCTION(profiler::colors::Red); - std::this_thread::sleep_for(std::chrono::milliseconds(20)); -} -void six() -{ - EASY_FUNCTION(profiler::colors::Red); - std::this_thread::sleep_for(std::chrono::milliseconds(42)); -} - -void three() -{ - EASY_FUNCTION(profiler::colors::Red); - four(); - five(); - six(); -} - -void seven() -{ - EASY_FUNCTION(profiler::colors::Red); - std::this_thread::sleep_for(std::chrono::milliseconds(147)); -} - -void two() -{ - EASY_FUNCTION(profiler::colors::Red); - std::this_thread::sleep_for(std::chrono::milliseconds(26)); -} - -void one() -{ - EASY_FUNCTION(profiler::colors::Red); - two(); - three(); - seven(); -} - -/* -one - two - three - four - five - six - seven -*/ +////////////////////////////////////////////////////////////////////////// int main(int argc, char* argv[]) { @@ -225,7 +172,7 @@ int main(int argc, char* argv[]) OBJECTS = std::atoi(argv[1]); } if (argc > 2 && argv[2]){ - RENDER_SPEPS = std::atoi(argv[2]); + RENDER_STEPS = std::atoi(argv[2]); } if (argc > 3 && argv[3]){ MODELLING_STEPS = std::atoi(argv[3]); @@ -235,16 +182,14 @@ int main(int argc, char* argv[]) } std::cout << "Objects count: " << OBJECTS << std::endl; - std::cout << "Render steps: " << RENDER_SPEPS << std::endl; + std::cout << "Render steps: " << RENDER_STEPS << std::endl; std::cout << "Modelling steps: " << MODELLING_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; - //one(); - //one(); - /**/ + std::vector threads; std::thread render = std::thread(renderThread); @@ -262,7 +207,7 @@ int main(int argc, char* argv[]) } cv.notify_all(); - for (int i = 0; i < RENDER_SPEPS; ++i) { + for (int i = 0; i < RENDER_STEPS; ++i) { modellingStep(); localSleep(1200000); } From 248348ee0573dee8de0b5c720e2c6834629ac170 Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Thu, 8 Sep 2016 22:42:35 +0300 Subject: [PATCH 02/25] (EasyGraphicsView) Added widget displaying thread names on scene. Double click on thread name makes it current selected thread. (EasyGraphicsView) Selected item's text is now painted bold + removed selected item color, but made selected item's border more wide. (profiler_gui) Colors adjustment. --- include/profiler/profiler_colors.h | 2 +- profiler_gui/blocks_graphics_view.cpp | 486 ++++++++++++++++---------- profiler_gui/blocks_graphics_view.h | 76 +++- profiler_gui/common_types.h | 9 +- profiler_gui/graphics_scrollbar.cpp | 2 +- sample/main.cpp | 2 +- src/event_trace_win.cpp | 2 +- 7 files changed, 380 insertions(+), 199 deletions(-) diff --git a/include/profiler/profiler_colors.h b/include/profiler/profiler_colors.h index 8107305..e441563 100644 --- a/include/profiler/profiler_colors.h +++ b/include/profiler/profiler_colors.h @@ -364,7 +364,7 @@ namespace profiler { const color_t Skin = Orange100; const color_t DeepOrange = DeepOrange500; const color_t Brick = DeepOrange900; - const color_t Coral = DeepOrange200; + const color_t Coral = DeepOrange300; const color_t Brown = Brown500; const color_t DarkBrown = Brown900; const color_t CreamWhite = Orange50; diff --git a/profiler_gui/blocks_graphics_view.cpp b/profiler_gui/blocks_graphics_view.cpp index d97905c..93e3f66 100644 --- a/profiler_gui/blocks_graphics_view.cpp +++ b/profiler_gui/blocks_graphics_view.cpp @@ -77,19 +77,29 @@ const unsigned short GRAPHICS_ROW_SIZE_FULL = GRAPHICS_ROW_SIZE + GRAPHICS_ROW_S const unsigned short THREADS_ROW_SPACING = 8; const unsigned short TIMELINE_ROW_SIZE = 20; -const QRgb BORDERS_COLOR = 0x00686868;// 0x00a07050; -const QRgb BACKGROUND_1 = 0x00dddddd; -const QRgb BACKGROUND_2 = 0x00ffffff; -const QRgb TIMELINE_BACKGROUND = 0x20303030; -const QRgb SELECTED_ITEM_COLOR = 0x000050a0; -const QColor CHRONOMETER_COLOR2 = QColor::fromRgba(0x40408040); +const QRgb BORDERS_COLOR = ::profiler::colors::Grey700 & 0x00ffffff;// 0x00686868; +const QRgb BACKGROUND_1 = ::profiler::colors::Grey300; +const QRgb BACKGROUND_2 = ::profiler::colors::White; +const QRgb TIMELINE_BACKGROUND = 0x20000000 | (::profiler::colors::Grey800 & 0x00ffffff);// 0x20303030; +//const QRgb SELECTED_ITEM_COLOR = ::profiler::colors::Dark;// 0x000050a0; +//const QRgb SELECTED_ITEM_BORDER_LIGHT = ::profiler::colors::RichRed; +const QRgb SELECTED_ITEM_BORDER_DARK = ::profiler::colors::Black; +const QColor CHRONOMETER_COLOR2 = QColor::fromRgba(0x40000000 | (::profiler::colors::Green700 & 0x00ffffff));// 0x40408040); + +inline QRgb selectedItemBorderColor(::profiler::color_t _color) +{ + //return ::profiler_gui::isLightColor(_color) ? SELECTED_ITEM_BORDER_DARK : SELECTED_ITEM_BORDER_LIGHT; + return SELECTED_ITEM_BORDER_DARK; +} //const unsigned int TEST_PROGRESSION_BASE = 4; const int FLICKER_INTERVAL = 16; // 60Hz -const auto CHRONOMETER_FONT = QFont("CourierNew", 18, 2); -const auto ITEMS_FONT = QFont("CourierNew", 10);// , 2); +const auto BG_FONT = QFont("CourierNew", 10, QFont::Bold); +const auto CHRONOMETER_FONT = QFont("CourierNew", 16, QFont::Bold); +const auto ITEMS_FONT = QFont("CourierNew", 10, QFont::Medium); +const auto SELECTED_ITEM_FONT = QFont("CourierNew", 10, QFont::Bold); ////////////////////////////////////////////////////////////////////////// @@ -108,9 +118,10 @@ inline T logn(T _value) ////////////////////////////////////////////////////////////////////////// -EasyGraphicsItem::EasyGraphicsItem(uint8_t _index, const::profiler::BlocksTreeRoot* _root) +EasyGraphicsItem::EasyGraphicsItem(uint8_t _index, const::profiler::BlocksTreeRoot& _root) : QGraphicsItem(nullptr) - , m_pRoot(_root) + , m_threadName(*_root.thread_name != 0 ? QString("%1 Thread %2").arg(_root.thread_name).arg(_root.thread_id) : QString("Thread %1").arg(_root.thread_id)) + , m_pRoot(&_root) , m_index(_index) { } @@ -288,78 +299,32 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* if (dh > 0) h -= dh; - bool changepen = false; if (item.block == EASY_GLOBALS.selected_block) - { selectedItemsWasPainted = true; - changepen = true; - QPen pen(Qt::SolidLine); - pen.setColor(Qt::red); - pen.setWidth(2); - _painter->setPen(pen); - previousColor = SELECTED_ITEM_COLOR; + const bool colorChange = (previousColor != item.color); + if (colorChange) + { + // Set background color brush for rectangle + previousColor = item.color; inverseColor = 0xffffffff - previousColor; textColor = ::profiler_gui::textColorForRgb(previousColor); brush.setColor(previousColor); _painter->setBrush(brush); } - else - { - const bool colorChange = (previousColor != item.color); - if (colorChange) - { - // Set background color brush for rectangle - previousColor = item.color; - inverseColor = 0xffffffff - previousColor; - textColor = ::profiler_gui::textColorForRgb(previousColor); - brush.setColor(previousColor); - _painter->setBrush(brush); - } - if (EASY_GLOBALS.draw_graphics_items_borders) - { - //if (w < 2) - //{ - // // Do not paint borders for very narrow items - // if (previousPenStyle != Qt::NoPen) - // { - // previousPenStyle = Qt::NoPen; - // _painter->setPen(Qt::NoPen); - // } - //} - //else - if (previousPenStyle != Qt::SolidLine || colorChange) - { - // Restore pen for item which is wide enough to paint borders - previousPenStyle = Qt::SolidLine; - _painter->setPen(BORDERS_COLOR & inverseColor);// BORDERS_COLOR); - } - } + if (EASY_GLOBALS.draw_graphics_items_borders && (previousPenStyle != Qt::SolidLine || colorChange)) + { + // Restore pen for item which is wide enough to paint borders + previousPenStyle = Qt::SolidLine; + _painter->setPen(BORDERS_COLOR & inverseColor);// BORDERS_COLOR); } if (w < 2) w = 2; - //if (w < 2 && !changepen) - //{ - // // Draw line - // changepen = true; - // _painter->setPen(previousColor); - // _painter->drawLine(QPointF(x, top), QPointF(x, top + h)); - //} - //else - { - // Draw rectangle - rect.setRect(x, top, w, h); - _painter->drawRect(rect); - } - if (changepen) - { - if (previousPenStyle == Qt::NoPen) - _painter->setPen(Qt::NoPen); - else - _painter->setPen(BORDERS_COLOR & inverseColor);// BORDERS_COLOR); // restore pen for rectangle painting - } + // Draw rectangle + rect.setRect(x, top, w, h); + _painter->drawRect(rect); prevRight = rect.right(); skip_children(next_level, item.children_begin); @@ -386,38 +351,24 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* } if (item.block == EASY_GLOBALS.selected_block) - { selectedItemsWasPainted = true; - QPen pen(Qt::SolidLine); - pen.setColor(Qt::red); - pen.setWidth(2); - _painter->setPen(pen); - previousColor = SELECTED_ITEM_COLOR; + const bool colorChange = (previousColor != item.color); + if (colorChange) + { + // Set background color brush for rectangle + previousColor = item.color; inverseColor = 0xffffffff - previousColor; textColor = ::profiler_gui::textColorForRgb(previousColor); brush.setColor(previousColor); _painter->setBrush(brush); } - else - { - const bool colorChange = (previousColor != item.color); - if (colorChange) - { - // Set background color brush for rectangle - previousColor = item.color; - inverseColor = 0xffffffff - previousColor; - textColor = ::profiler_gui::textColorForRgb(previousColor); - brush.setColor(previousColor); - _painter->setBrush(brush); - } - if (EASY_GLOBALS.draw_graphics_items_borders && (previousPenStyle != Qt::SolidLine || colorChange)) - { - // Restore pen for item which is wide enough to paint borders - previousPenStyle = Qt::SolidLine; - _painter->setPen(BORDERS_COLOR & inverseColor);// BORDERS_COLOR); - } + if (EASY_GLOBALS.draw_graphics_items_borders && (previousPenStyle != Qt::SolidLine || colorChange)) + { + // Restore pen for item which is wide enough to paint borders + previousPenStyle = Qt::SolidLine; + _painter->setPen(BORDERS_COLOR & inverseColor);// BORDERS_COLOR); } // Draw rectangle @@ -459,6 +410,9 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* //if (textColor == previousColor) textColor = 0; _painter->setPen(QColor::fromRgb(textColor)); + if (item.block == EASY_GLOBALS.selected_block) + _painter->setFont(SELECTED_ITEM_FONT); + // drawing text auto name = *itemBlock.tree.node->name() != 0 ? itemBlock.tree.node->name() : easyDescriptor(itemBlock.tree.node->id()).name(); _painter->drawText(rect, flags, ::profiler_gui::toUnicode(name)); @@ -468,11 +422,15 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* _painter->setPen(Qt::NoPen); else _painter->setPen(BORDERS_COLOR & inverseColor);// BORDERS_COLOR); // restore pen for rectangle painting + + // restore font + if (item.block == EASY_GLOBALS.selected_block) + _painter->setFont(ITEMS_FONT); // END Draw text~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ } } - if (!selectedItemsWasPainted && EASY_GLOBALS.selected_block < EASY_GLOBALS.gui_blocks.size()) + if (EASY_GLOBALS.selected_block < EASY_GLOBALS.gui_blocks.size()) { const auto& guiblock = EASY_GLOBALS.gui_blocks[EASY_GLOBALS.selected_block]; if (guiblock.graphics_item == m_index) @@ -481,7 +439,8 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* if (item.left() < sceneRight && item.right() > sceneLeft) { auto top = levelY(guiblock.graphics_item_level); - decltype(top) h = item.totalHeight; + auto w = ::std::max(item.width() * currentScale, 1.0); + decltype(top) h = (selectedItemsWasPainted && easyBlock(item.block).expanded && w > 20) ? GRAPHICS_ROW_SIZE : item.totalHeight; auto dh = top + h - visibleBottom; if (dh < h) @@ -490,19 +449,26 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* h -= dh; QPen pen(Qt::SolidLine); - pen.setColor(Qt::red); - pen.setWidth(2); + pen.setJoinStyle(Qt::MiterJoin); + pen.setColor(selectedItemBorderColor(item.color));//Qt::red); + pen.setWidth(3); _painter->setPen(pen); - brush.setColor(SELECTED_ITEM_COLOR); - _painter->setBrush(brush); + if (!selectedItemsWasPainted) + { + brush.setColor(item.color);// SELECTED_ITEM_COLOR); + _painter->setBrush(brush); + } + else + { + _painter->setBrush(Qt::NoBrush); + } auto x = item.left() * currentScale - dx; - auto w = ::std::max(item.width() * currentScale, 1.0); rect.setRect(x, top, w, h); _painter->drawRect(rect); - if (w > 20) + if (!selectedItemsWasPainted && w > 20) { // Draw text----------------------------------- // calculating text coordinates @@ -525,9 +491,11 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* // text will be painted with inverse color //auto textColor = 0x00ffffff - previousColor; //if (textColor == previousColor) textColor = 0; - textColor = ::profiler_gui::textColorForRgb(SELECTED_ITEM_COLOR); + textColor = ::profiler_gui::textColorForRgb(item.color);// SELECTED_ITEM_COLOR); _painter->setPen(textColor); + _painter->setFont(SELECTED_ITEM_FONT); + // drawing text const auto& itemBlock = easyBlock(item.block); auto name = *itemBlock.tree.node->name() != 0 ? itemBlock.tree.node->name() : easyDescriptor(itemBlock.tree.node->id()).name(); @@ -543,8 +511,8 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* #ifdef EASY_STORE_CSWITCH_SEPARATELY if (!m_pRoot->sync.empty()) { - _painter->setBrush(QColor::fromRgba(0xfffe6030)); - _painter->setPen(QColor::fromRgb(0x00505050)); + _painter->setBrush(QColor::fromRgb(::profiler::colors::Coral));// 0xfffe6030)); + _painter->setPen(QColor::fromRgb(::profiler::colors::Grey800));// 0x00505050)); qreal prevRight = -1e100, top = y() - 4, h = 3; if (top + h < visibleBottom) @@ -593,6 +561,18 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* ////////////////////////////////////////////////////////////////////////// +const ::profiler::BlocksTreeRoot* EasyGraphicsItem::root() const +{ + return m_pRoot; +} + +const QString& EasyGraphicsItem::threadName() const +{ + return m_threadName; +} + +////////////////////////////////////////////////////////////////////////// + QRect EasyGraphicsItem::getRect() const { return view()->mapFromScene(m_boundingRect).boundingRect(); @@ -1454,20 +1434,18 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr // Calculating start and end time ::profiler::timestamp_t finish = 0; - ::profiler::thread_id_t longestTree = 0; - const EasyGraphicsItem* longestItem = nullptr; - const EasyGraphicsItem* mainThreadItem = nullptr; + ::profiler::thread_id_t longestTree = 0, mainTree = 0; for (const auto& threadTree : _blocksTree) { - const auto& tree = threadTree.second.children; - auto timestart = blocksTree(tree.front()).node->begin(); + const auto& t = threadTree.second; + auto timestart = blocksTree(t.children.front()).node->begin(); #ifdef EASY_STORE_CSWITCH_SEPARATELY - if (!threadTree.second.sync.empty()) - timestart = ::std::min(timestart, blocksTree(threadTree.second.sync.front()).node->begin()); + if (!t.sync.empty()) + timestart = ::std::min(timestart, blocksTree(t.sync.front()).node->begin()); #endif - const auto timefinish = blocksTree(tree.back()).node->end(); + const auto timefinish = blocksTree(t.children.back()).node->end(); if (m_beginTime > timestart) { @@ -1479,11 +1457,17 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr finish = timefinish; longestTree = threadTree.first; } + + if (mainTree == 0 && !strcmp(t.thread_name, "Main")) + { + mainTree = threadTree.first; + } } // Filling scene with items m_items.reserve(_blocksTree.size()); qreal y = TIMELINE_ROW_SIZE; + const EasyGraphicsItem *longestItem = nullptr, *mainThreadItem = nullptr; for (const auto& threadTree : _blocksTree) { if (m_items.size() == 0xff) @@ -1492,11 +1476,13 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr break; } + const auto& t = threadTree.second; + // fill scene with new items - const auto& tree = threadTree.second.children; + const auto& tree = t.children; qreal h = 0, x = time2position(blocksTree(tree.front()).node->begin()); - auto item = new EasyGraphicsItem(static_cast(m_items.size()), &threadTree.second); - item->setLevels(threadTree.second.depth); + auto item = new EasyGraphicsItem(static_cast(m_items.size()), t); + item->setLevels(t.depth); item->setPos(0, y); const auto children_duration = setTree(item, tree, h, y, 0); @@ -1510,7 +1496,7 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr if (longestTree == threadTree.first) longestItem = item; - if (mainThreadItem == nullptr && !strcmp(threadTree.second.thread_name, "Main")) + if (mainTree == threadTree.first) mainThreadItem = item; } @@ -1524,16 +1510,6 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr updateVisibleSceneRect(); setScrollbar(m_pScrollbar); - if (mainThreadItem != nullptr) - longestItem = mainThreadItem; - - if (longestItem != nullptr) - { - m_pScrollbar->setMinimapFrom(longestItem->threadId(), longestItem->items(0)); - EASY_GLOBALS.selected_thread = longestItem->threadId(); - emit EASY_GLOBALS.events.selectedThreadChanged(longestItem->threadId()); - } - // Create new chronometer item (previous item was destroyed by scene on scene()->clear()). // It will be shown on mouse right button click. m_chronometerItemAux = createChronometer(false); @@ -1549,6 +1525,19 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr m_bEmpty = false; scaleTo(BASE_SCALE); + + + emit treeChanged(); + + if (mainThreadItem != nullptr) + longestItem = mainThreadItem; + + if (longestItem != nullptr) + { + m_pScrollbar->setMinimapFrom(longestItem->threadId(), longestItem->items(0)); + EASY_GLOBALS.selected_thread = longestItem->threadId(); + emit EASY_GLOBALS.events.selectedThreadChanged(longestItem->threadId()); + } } const EasyGraphicsView::Items &EasyGraphicsView::getItems() const @@ -1910,26 +1899,29 @@ void EasyGraphicsView::mouseReleaseEvent(QMouseEvent* _event) //clicked = true; auto mouseClickPos = mapToScene(m_mousePressPos); - mouseClickPos.setX(m_offset + mouseClickPos.x() / m_scale); - - // Try to select one of item blocks - for (auto item : m_items) + if (mouseClickPos.x() >= 0) { - auto block = item->intersect(mouseClickPos); - if (block) + mouseClickPos.setX(m_offset + mouseClickPos.x() / m_scale); + + // Try to select one of item blocks + for (auto item : m_items) + { + auto block = item->intersect(mouseClickPos); + if (block) + { + changedSelectedItem = true; + selectedBlock = block; + EASY_GLOBALS.selected_block = block->block; + break; + } + } + + if (!changedSelectedItem && EASY_GLOBALS.selected_block != ::profiler_gui::numeric_max(EASY_GLOBALS.selected_block)) { changedSelectedItem = true; - selectedBlock = block; - EASY_GLOBALS.selected_block = block->block; - break; + ::profiler_gui::set_max(EASY_GLOBALS.selected_block); } } - - if (!changedSelectedItem && EASY_GLOBALS.selected_block != ::profiler_gui::numeric_max(EASY_GLOBALS.selected_block)) - { - changedSelectedItem = true; - ::profiler_gui::set_max(EASY_GLOBALS.selected_block); - } } } @@ -2019,10 +2011,11 @@ void EasyGraphicsView::mouseMoveEvent(QMouseEvent* _event) auto mouseScenePos = mapToScene(m_mousePressPos); mouseScenePos.setX(m_offset + mouseScenePos.x() / m_scale); + const auto x = clamp(0., mouseScenePos.x(), sceneRect().width()); if (m_mouseButtons & Qt::RightButton) { - bool showItem = moveChrono(m_chronometerItem, mouseScenePos.x()); + bool showItem = moveChrono(m_chronometerItem, x); m_pScrollbar->setChronoPos(m_chronometerItem->left(), m_chronometerItem->right()); if (showItem) @@ -2037,7 +2030,7 @@ void EasyGraphicsView::mouseMoveEvent(QMouseEvent* _event) { if (m_bDoubleClick) { - moveChrono(m_chronometerItemAux, mouseScenePos.x()); + moveChrono(m_chronometerItemAux, x); } else { @@ -2243,9 +2236,9 @@ void EasyGraphicsView::onItemsEspandStateChange() EasyGraphicsViewWidget::EasyGraphicsViewWidget(QWidget* _parent) : QWidget(_parent) - , m_scrollbar(new EasyGraphicsScrollbar(nullptr)) - , m_view(new EasyGraphicsView(nullptr)) - //, m_threadWidget(new EasyThreadViewWidget(this,m_view)) + , m_scrollbar(new EasyGraphicsScrollbar(this)) + , m_view(new EasyGraphicsView(this)) + , m_threadNamesWidget(new EasyThreadNamesWidget(m_view, this)) { initWidget(); } @@ -2254,11 +2247,11 @@ void EasyGraphicsViewWidget::initWidget() { auto lay = new QGridLayout(this); lay->setContentsMargins(1, 0, 1, 0); + lay->addWidget(m_threadNamesWidget, 0, 0); + lay->setSpacing(1); lay->addWidget(m_view, 0, 1); lay->setSpacing(1); lay->addWidget(m_scrollbar, 1, 1); - //lay->setSpacing(1); - //lay->addWidget(m_threadWidget, 0, 0); setLayout(lay); m_view->setScrollbar(m_scrollbar); @@ -2277,47 +2270,186 @@ EasyGraphicsView* EasyGraphicsViewWidget::view() ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// -EasyThreadViewWidget::EasyThreadViewWidget(QWidget *parent, EasyGraphicsView* view):QWidget(parent), - m_view(view) - , m_label(new QLabel("",this)) +EasyThreadNameItem::EasyThreadNameItem() : QGraphicsItem(nullptr) { - m_layout = new QHBoxLayout; - //QPushButton *button1 = new QPushButton(); - //m_layout->addWidget(m_label); - //setLayout(m_layout); - //show(); +} + +EasyThreadNameItem::~EasyThreadNameItem() +{ + +} + +QRectF EasyThreadNameItem::boundingRect() const +{ + return m_boundingRect; +} + +void EasyThreadNameItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*) +{ + const auto view = static_cast(scene()->parent())->view(); + const auto& items = view->getItems(); + if (items.empty()) + return; + + const auto visibleSceneRect = view->visibleSceneRect(); + const auto h = visibleSceneRect.height() + TIMELINE_ROW_SIZE; + const auto w = scene()->sceneRect().width(); + + static const auto OVERLAP = THREADS_ROW_SPACING >> 1; + static const QBrush brushes[2] = {QColor::fromRgb(BACKGROUND_1), QColor::fromRgb(BACKGROUND_2)}; + int i = -1; + + QRectF rect; + + _painter->resetTransform(); + _painter->setFont(BG_FONT); + + for (auto item : items) + { + ++i; + + auto br = item->boundingRect(); + auto top = item->y() + br.top() - visibleSceneRect.top() - OVERLAP; + auto hgt = br.height() + THREADS_ROW_SPACING; + auto bottom = top + hgt; + + if (top > h || bottom < 0) + continue; + + if (item->threadId() == EASY_GLOBALS.selected_thread) + _painter->setBrush(QBrush(QColor::fromRgb(::profiler_gui::SELECTED_THREAD_BACKGROUND))); + else + _painter->setBrush(brushes[i & 1]); + + const auto dh = rect.bottom() - h; + if (dh > 0) hgt -= dh; + if (top < 0) { hgt += top; top = 0; } + rect.setRect(0, top, w, hgt); + + _painter->setPen(Qt::NoPen); + _painter->drawRect(rect); + + rect.translate(-5, 0); + _painter->setPen(QColor::fromRgb(::profiler::colors::Dark)); + _painter->drawText(rect, Qt::AlignRight | Qt::AlignVCenter, item->threadName()); + } +} + +void EasyThreadNameItem::setBoundingRect(const QRectF& _rect) +{ + m_boundingRect = _rect; +} + +////////////////////////////////////////////////////////////////////////// + +EasyThreadNamesWidget::EasyThreadNamesWidget(EasyGraphicsView* _view, QWidget* _parent) : Parent(_parent) + , m_view(_view) +{ + setScene(new QGraphicsScene(this)); + + setCacheMode(QGraphicsView::CacheNone); + setViewportUpdateMode(QGraphicsView::FullViewportUpdate); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setFixedWidth(100); + connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedThreadChanged, this, &This::onSelectedThreadChange); + connect(m_view, &EasyGraphicsView::treeChanged, this, &This::onTreeChange); + connect(m_view->verticalScrollBar(), &QScrollBar::valueChanged, verticalScrollBar(), &QScrollBar::setValue); + connect(m_view->verticalScrollBar(), &QScrollBar::rangeChanged, verticalScrollBar(), &QScrollBar::setRange); } -EasyThreadViewWidget::~EasyThreadViewWidget() +EasyThreadNamesWidget::~EasyThreadNamesWidget() { } -void EasyThreadViewWidget::onSelectedThreadChange(::profiler::thread_id_t _id) +void EasyThreadNamesWidget::onTreeChange() { -/* - auto threadName = EASY_GLOBALS.profiler_blocks[EASY_GLOBALS.selected_thread].thread_name; - if(threadName[0]!=0) - { - m_label->setText(threadName); - } - else - { - m_label->setText(QString("Thread %1").arg(EASY_GLOBALS.selected_thread)); - } -*/ - QLayoutItem *ditem; - while ((ditem = m_layout->takeAt(0))) - delete ditem; + QSignalBlocker b(this); + scene()->clear(); + QFontMetricsF fm(BG_FONT); + qreal maxLength = 0; + const auto& graphicsItems = m_view->getItems(); + for (auto graphicsItem : graphicsItems) + maxLength = ::std::max(maxLength, fm.boundingRect(graphicsItem->threadName()).width()); + + auto vbar = verticalScrollBar(); + auto viewBar = m_view->verticalScrollBar(); + + vbar->setRange(viewBar->minimum(), viewBar->maximum()); + vbar->setSingleStep(viewBar->singleStep()); + vbar->setPageStep(viewBar->pageStep()); + + auto r = m_view->sceneRect(); + setSceneRect(0, r.top(), maxLength, r.height()); + + auto item = new EasyThreadNameItem(); + item->setPos(0, 0); + item->setBoundingRect(sceneRect()); + scene()->addItem(item); + + b.unblock(); + setFixedWidth(maxLength); + scene()->update(); +} + +void EasyThreadNamesWidget::onSelectedThreadChange(::profiler::thread_id_t) +{ + scene()->update(); +} + +void EasyThreadNamesWidget::mousePressEvent(QMouseEvent* _event) +{ + QMouseEvent e(_event->type(), _event->pos() - QPointF(sceneRect().width(), 0), _event->button(), _event->buttons() & ~Qt::RightButton, _event->modifiers()); + m_view->mousePressEvent(&e); + _event->accept(); +} + +void EasyThreadNamesWidget::mouseDoubleClickEvent(QMouseEvent* _event) +{ + static const auto OVERLAP = THREADS_ROW_SPACING >> 1; + + auto y = mapToScene(_event->pos()).y(); const auto& items = m_view->getItems(); - for(const auto& item: items) + for (auto item : items) { - m_layout->addWidget(new QLabel(QString("Thread %1").arg(item->threadId()))); - m_layout->setSpacing(1); - } - setLayout(m_layout); + auto br = item->boundingRect(); + auto top = item->y() + br.top() - OVERLAP; + auto bottom = top + br.height() + OVERLAP; + if (y < top || y > bottom) + continue; + + const auto thread_id = item->threadId(); + if (thread_id != EASY_GLOBALS.selected_thread) + { + EASY_GLOBALS.selected_thread = thread_id; + emit EASY_GLOBALS.events.selectedThreadChanged(thread_id); + } + + break; + } + + _event->accept(); } + +void EasyThreadNamesWidget::mouseReleaseEvent(QMouseEvent* _event) +{ + QMouseEvent e(_event->type(), _event->pos() - QPointF(sceneRect().width(), 0), _event->button(), _event->buttons() & ~Qt::RightButton, _event->modifiers()); + m_view->mouseReleaseEvent(&e); + _event->accept(); +} + +void EasyThreadNamesWidget::mouseMoveEvent(QMouseEvent* _event) +{ + QMouseEvent e(_event->type(), _event->pos() - QPointF(sceneRect().width(), 0), _event->button(), _event->buttons() & ~Qt::RightButton, _event->modifiers()); + m_view->mouseMoveEvent(&e); + _event->accept(); +} + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + diff --git a/profiler_gui/blocks_graphics_view.h b/profiler_gui/blocks_graphics_view.h index 26d57dc..3344976 100644 --- a/profiler_gui/blocks_graphics_view.h +++ b/profiler_gui/blocks_graphics_view.h @@ -79,12 +79,13 @@ class EasyGraphicsItem : public QGraphicsItem Sublevels m_levels; ///< Arrays of items for each level QRectF m_boundingRect; ///< boundingRect (see QGraphicsItem) + QString m_threadName; ///< const ::profiler::BlocksTreeRoot* m_pRoot; ///< Pointer to the root profiler block (thread block). Used by ProfTreeWidget to restore hierarchy. uint8_t m_index; ///< This item's index in the list of items of EasyGraphicsView public: - EasyGraphicsItem(uint8_t _index, const::profiler::BlocksTreeRoot* _root); + explicit EasyGraphicsItem(uint8_t _index, const::profiler::BlocksTreeRoot& _root); virtual ~EasyGraphicsItem(); // Public virtual methods @@ -97,6 +98,9 @@ public: // Public non-virtual methods + const ::profiler::BlocksTreeRoot* root() const; + const QString& threadName() const; + QRect getRect() const; void setBoundingRect(qreal x, qreal y, qreal w, qreal h); @@ -324,6 +328,7 @@ signals: // Signals + void treeChanged(); void intervalChanged(const ::profiler_gui::TreeBlocks& _blocks, ::profiler::timestamp_t _session_begin_time, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict); private: @@ -396,23 +401,62 @@ public: ////////////////////////////////////////////////////////////////////////// -class EasyThreadViewWidget : public QWidget +class EasyThreadNameItem : public QGraphicsItem { - Q_OBJECT -private: - EasyGraphicsView* m_view; - QLabel* m_label; - typedef EasyThreadViewWidget This; - - QHBoxLayout *m_layout; + QRectF m_boundingRect; ///< boundingRect (see QGraphicsItem) public: - EasyThreadViewWidget(QWidget *parent, EasyGraphicsView* view); - virtual ~EasyThreadViewWidget(); -public slots: - void onSelectedThreadChange(::profiler::thread_id_t _id); + + explicit EasyThreadNameItem(); + virtual ~EasyThreadNameItem(); + + // Public virtual methods + + QRectF boundingRect() const override; + + void paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget = nullptr) override; + +public: + + // Public non-virtual methods + + void setBoundingRect(const QRectF& _rect); + }; +class EasyThreadNamesWidget : public QGraphicsView +{ + Q_OBJECT + +private: + + typedef QGraphicsView Parent; + typedef EasyThreadNamesWidget This; + + EasyGraphicsView* m_view; + +public: + + explicit EasyThreadNamesWidget(EasyGraphicsView* _view, QWidget* _parent = nullptr); + virtual ~EasyThreadNamesWidget(); + + void mousePressEvent(QMouseEvent* _event) override; + void mouseDoubleClickEvent(QMouseEvent* _event) override; + void mouseReleaseEvent(QMouseEvent* _event) override; + void mouseMoveEvent(QMouseEvent* _event) override; + + const EasyGraphicsView* view() const + { + return m_view; + } + +private slots: + + void onTreeChange(); + void onSelectedThreadChange(::profiler::thread_id_t _id); + +}; // END of class EasyThreadNamesWidget. + ////////////////////////////////////////////////////////////////////////// class EasyGraphicsViewWidget : public QWidget @@ -421,9 +465,9 @@ class EasyGraphicsViewWidget : public QWidget private: - EasyGraphicsView* m_view; - EasyGraphicsScrollbar* m_scrollbar; - //EasyThreadViewWidget* m_threadWidget; + EasyGraphicsScrollbar* m_scrollbar; + EasyGraphicsView* m_view; + EasyThreadNamesWidget* m_threadNamesWidget; public: diff --git a/profiler_gui/common_types.h b/profiler_gui/common_types.h index ab84095..55a9bef 100644 --- a/profiler_gui/common_types.h +++ b/profiler_gui/common_types.h @@ -113,10 +113,15 @@ inline QRgb fromProfilerRgb(uint32_t _red, uint32_t _green, uint32_t _blue) return toRgb(_red, _green, _blue) | 0x00141414; } -inline ::profiler::color_t textColorForRgb(::profiler::color_t _color) +inline bool isLightColor(::profiler::color_t _color) { const auto sum = 255. - (((_color & 0x00ff0000) >> 16) * 0.299 + ((_color & 0x0000ff00) >> 8) * 0.587 + (_color & 0x000000ff) * 0.114); - return sum < 76.5 || ((_color & 0xff000000) >> 24) < 0x80 ? ::profiler::colors::Dark : ::profiler::colors::CreamWhite; + return sum < 76.5 || ((_color & 0xff000000) >> 24) < 0x80; +} + +inline ::profiler::color_t textColorForRgb(::profiler::color_t _color) +{ + return isLightColor(_color) ? ::profiler::colors::Dark : ::profiler::colors::CreamWhite; } ////////////////////////////////////////////////////////////////////////// diff --git a/profiler_gui/graphics_scrollbar.cpp b/profiler_gui/graphics_scrollbar.cpp index 0cf6c96..753e631 100644 --- a/profiler_gui/graphics_scrollbar.cpp +++ b/profiler_gui/graphics_scrollbar.cpp @@ -546,7 +546,7 @@ void EasyGraphicsScrollbar::onThreadActionClicked(bool) return; const auto thread_id = action->data().toUInt(); - if (thread_id != m_minimap->threadId()) + if (thread_id != EASY_GLOBALS.selected_thread) { EASY_GLOBALS.selected_thread = thread_id; emit EASY_GLOBALS.events.selectedThreadChanged(thread_id); diff --git a/sample/main.cpp b/sample/main.cpp index 350ac99..143fb94 100644 --- a/sample/main.cpp +++ b/sample/main.cpp @@ -56,7 +56,7 @@ void calcIntersect(){ double multModel(double i) { - EASY_FUNCTION(profiler::colors::Blue); + EASY_FUNCTION(profiler::colors::PaleGold); return i * sin(i) * cos(i); } diff --git a/src/event_trace_win.cpp b/src/event_trace_win.cpp index 804d4f3..0b947d3 100644 --- a/src/event_trace_win.cpp +++ b/src/event_trace_win.cpp @@ -144,7 +144,7 @@ namespace profiler { // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364093(v=vs.85).aspx m_stubThread = ::std::move(::std::thread([this]() { - EASY_THREAD("EasyProfiler.EventTracing"); + EASY_THREAD("EasyProfiler.ETW"); //EASY_BLOCK("ProcessTrace()", ::profiler::colors::Red); ProcessTrace(&m_openedHandle, 1, 0, 0); })); From 5b620071b69103391aa75a6de171cad38eccab33 Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Fri, 9 Sep 2016 00:07:27 +0300 Subject: [PATCH 03/25] (profiler_gui) Adjust colors; Added keyboard handlers; Displaying additional info --- profiler_gui/blocks_graphics_view.cpp | 390 ++++++++++++-------------- profiler_gui/blocks_graphics_view.h | 87 +++--- profiler_gui/common_types.h | 6 + profiler_gui/globals.h | 2 +- 4 files changed, 242 insertions(+), 243 deletions(-) diff --git a/profiler_gui/blocks_graphics_view.cpp b/profiler_gui/blocks_graphics_view.cpp index 93e3f66..b33a9f5 100644 --- a/profiler_gui/blocks_graphics_view.cpp +++ b/profiler_gui/blocks_graphics_view.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -71,25 +72,23 @@ const qreal MIN_SCALE = pow(::profiler_gui::SCALING_COEFFICIENT_INV, 70); // Up const qreal MAX_SCALE = pow(::profiler_gui::SCALING_COEFFICIENT, 45); // ~23000 --- Up to 10 ns scale const qreal BASE_SCALE = pow(::profiler_gui::SCALING_COEFFICIENT_INV, 25); // ~0.003 -const unsigned short GRAPHICS_ROW_SIZE = 18; -const unsigned short GRAPHICS_ROW_SPACING = 2; -const unsigned short GRAPHICS_ROW_SIZE_FULL = GRAPHICS_ROW_SIZE + GRAPHICS_ROW_SPACING; -const unsigned short THREADS_ROW_SPACING = 8; -const unsigned short TIMELINE_ROW_SIZE = 20; +const uint16_t GRAPHICS_ROW_SIZE = 18; +const uint16_t GRAPHICS_ROW_SPACING = 2; +const uint16_t GRAPHICS_ROW_SIZE_FULL = GRAPHICS_ROW_SIZE + GRAPHICS_ROW_SPACING; +const uint16_t THREADS_ROW_SPACING = 8; +const uint16_t TIMELINE_ROW_SIZE = 20; const QRgb BORDERS_COLOR = ::profiler::colors::Grey700 & 0x00ffffff;// 0x00686868; const QRgb BACKGROUND_1 = ::profiler::colors::Grey300; const QRgb BACKGROUND_2 = ::profiler::colors::White; const QRgb TIMELINE_BACKGROUND = 0x20000000 | (::profiler::colors::Grey800 & 0x00ffffff);// 0x20303030; //const QRgb SELECTED_ITEM_COLOR = ::profiler::colors::Dark;// 0x000050a0; -//const QRgb SELECTED_ITEM_BORDER_LIGHT = ::profiler::colors::RichRed; -const QRgb SELECTED_ITEM_BORDER_DARK = ::profiler::colors::Black; -const QColor CHRONOMETER_COLOR2 = QColor::fromRgba(0x40000000 | (::profiler::colors::Green700 & 0x00ffffff));// 0x40408040); +const QColor CHRONOMETER_COLOR2 = QColor::fromRgba(0x40000000 | (::profiler::colors::Dark & 0x00ffffff));// 0x40408040); inline QRgb selectedItemBorderColor(::profiler::color_t _color) { - //return ::profiler_gui::isLightColor(_color) ? SELECTED_ITEM_BORDER_DARK : SELECTED_ITEM_BORDER_LIGHT; - return SELECTED_ITEM_BORDER_DARK; + return ::profiler_gui::isLightColor(_color, 192) ? ::profiler::colors::Black : ::profiler::colors::RichRed; + //return ::profiler::colors::Black; } //const unsigned int TEST_PROGRESSION_BASE = 4; @@ -146,7 +145,7 @@ QRectF EasyGraphicsItem::boundingRect() const ////////////////////////////////////////////////////////////////////////// -void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget) +void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*) { if (m_levels.empty() || m_levels.front().empty()) { @@ -194,7 +193,6 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* } -#ifdef EASY_STORE_CSWITCH_SEPARATELY auto firstSync = ::std::lower_bound(m_pRoot->sync.begin(), m_pRoot->sync.end(), sceneLeft, [&sceneView](::profiler::block_index_t _index, qreal _value) { return sceneView->time2position(easyBlock(_index).tree.node->begin()) < _value; @@ -210,7 +208,6 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* firstSync = m_pRoot->sync.begin() + m_pRoot->sync.size() - 1; } firstSync = m_pRoot->sync.begin(); -#endif // This is to make _painter->drawText() work properly @@ -508,7 +505,6 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* } -#ifdef EASY_STORE_CSWITCH_SEPARATELY if (!m_pRoot->sync.empty()) { _painter->setBrush(QColor::fromRgb(::profiler::colors::Coral));// 0xfffe6030)); @@ -553,7 +549,6 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* } } } -#endif _painter->restore(); @@ -843,7 +838,7 @@ QRectF EasyChronometerItem::boundingRect() const return m_boundingRect; } -void EasyChronometerItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget) +void EasyChronometerItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*) { const auto sceneView = view(); const auto currentScale = sceneView->scale(); @@ -919,8 +914,8 @@ void EasyChronometerItem::paint(QPainter* _painter, const QStyleOptionGraphicsIt auto vcenter = rect.top() + rect.height() * 0.5; QLinearGradient g(rect.left(), vcenter, rect.right(), vcenter); g.setColorAt(0, m_color); - g.setColorAt(0.25, QColor::fromRgba(0x20000000 | rgb)); - g.setColorAt(0.75, QColor::fromRgba(0x20000000 | rgb)); + g.setColorAt(0.2, QColor::fromRgba(0x14000000 | rgb)); + g.setColorAt(0.8, QColor::fromRgba(0x14000000 | rgb)); g.setColorAt(1, m_color); _painter->setBrush(g); _painter->setPen(Qt::NoPen); @@ -929,6 +924,7 @@ void EasyChronometerItem::paint(QPainter* _painter, const QStyleOptionGraphicsIt // draw left and right borders _painter->setBrush(Qt::NoBrush); _painter->setPen(QColor::fromRgba(0xd0000000 | rgb)); + if (m_left > sceneLeft) _painter->drawLine(QPointF(rect.left(), rect.top()), QPointF(rect.left(), rect.bottom())); if (m_right < sceneRight) @@ -1045,7 +1041,7 @@ const EasyGraphicsView* EasyChronometerItem::view() const ////////////////////////////////////////////////////////////////////////// -void EasyBackgroundItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget) +void EasyBackgroundItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*) { const auto sceneView = static_cast(scene()->parent()); const auto visibleSceneRect = sceneView->visibleSceneRect(); @@ -1063,7 +1059,7 @@ void EasyBackgroundItem::paint(QPainter* _painter, const QStyleOptionGraphicsIte const auto& items = sceneView->getItems(); if (!items.empty()) { - static const auto OVERLAP = THREADS_ROW_SPACING >> 1; + static const uint16_t OVERLAP = THREADS_ROW_SPACING >> 1; static const QBrush brushes[2] = {QColor::fromRgb(BACKGROUND_1), QColor::fromRgb(BACKGROUND_2)}; int i = -1; @@ -1160,7 +1156,7 @@ void EasyBackgroundItem::paint(QPainter* _painter, const QStyleOptionGraphicsIte ////////////////////////////////////////////////////////////////////////// -void EasyTimelineIndicatorItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget) +void EasyTimelineIndicatorItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*) { const auto sceneView = static_cast(scene()->parent()); const auto visibleSceneRect = sceneView->visibleSceneRect(); @@ -1205,7 +1201,6 @@ EasyGraphicsView::EasyGraphicsView(QWidget* _parent) , m_flickerSpeedY(0) , m_bDoubleClick(false) , m_bUpdatingRect(false) - //, m_bTest(false) , m_bEmpty(true) { initMode(); @@ -1232,163 +1227,6 @@ EasyChronometerItem* EasyGraphicsView::createChronometer(bool _main) ////////////////////////////////////////////////////////////////////////// -/*void EasyGraphicsView::fillTestChildren(EasyGraphicsItem* _item, const int _maxlevel, int _level, qreal _x, unsigned int _childrenNumber, unsigned int& _total_items) -{ - unsigned int nchildren = _childrenNumber; - _childrenNumber = TEST_PROGRESSION_BASE; - - for (unsigned int i = 0; i < nchildren; ++i) - { - auto j = _item->addItem(_level); - auto& b = _item->getItem(_level, j); - b.color = ::profiler_gui::toRgb(30 + rand() % 225, 30 + rand() % 225, 30 + rand() % 225); - b.state = 0; - - if (_level < _maxlevel) - { - const auto& children = _item->items(_level + 1); - b.children_begin = static_cast(children.size()); - - fillTestChildren(_item, _maxlevel, _level + 1, _x, _childrenNumber, _total_items); - - const auto& last = children.back(); - b.setPos(_x, last.right() - _x); - b.totalHeight = GRAPHICS_ROW_SIZE_FULL + last.totalHeight; - } - else - { - b.setPos(_x, units2microseconds(10 + rand() % 190)); - b.totalHeight = GRAPHICS_ROW_SIZE; - ::profiler_gui::set_max(b.children_begin); - } - - _x = b.right(); - ++_total_items; - } -} - -void EasyGraphicsView::test(unsigned int _frames_number, unsigned int _total_items_number_estimate, uint8_t _rows) -{ - static const qreal X_BEGIN = 50; - static const qreal Y_BEGIN = 0; - - clearSilent(); // Clear scene - - // Calculate items number for first level - _rows = ::std::max((uint8_t)1, _rows); - const auto children_per_frame = static_cast(0.5 + static_cast(_total_items_number_estimate) / static_cast(_rows * _frames_number)); - const uint8_t max_depth = ::std::min(254, static_cast(logn(children_per_frame * (TEST_PROGRESSION_BASE - 1) * 0.5 + 1))); - const auto first_level_children_count = static_cast(static_cast(children_per_frame) * (1.0 - TEST_PROGRESSION_BASE) / (1.0 - pow(TEST_PROGRESSION_BASE, max_depth)) + 0.5); - - - auto bgItem = new EasyBackgroundItem(); - scene()->addItem(bgItem); - - - ::std::vector thread_items(_rows); - for (uint8_t i = 0; i < _rows; ++i) - { - auto item = new EasyGraphicsItem(i, true); - thread_items[i] = item; - - item->setPos(0, Y_BEGIN + i * (max_depth * GRAPHICS_ROW_SIZE_FULL + THREADS_ROW_SPACING * 5)); - - item->setLevels(max_depth + 1); - item->reserve(0, _frames_number); - } - - // Calculate items number for each sublevel - auto chldrn = first_level_children_count; - for (uint8_t i = 1; i <= max_depth; ++i) - { - for (uint8_t j = 0; j < _rows; ++j) - { - auto item = thread_items[j]; - item->reserve(i, chldrn * _frames_number); - } - - chldrn *= TEST_PROGRESSION_BASE; - } - - // Create required number of items - unsigned int total_items = 0; - qreal maxX = 0; - const EasyGraphicsItem* longestItem = nullptr; - for (uint8_t i = 0; i < _rows; ++i) - { - auto item = thread_items[i]; - qreal x = X_BEGIN, y = item->y(); - for (unsigned int f = 0; f < _frames_number; ++f) - { - auto j = item->addItem(0); - auto& b = item->getItem(0, j); - b.color = ::profiler_gui::toRgb(30 + rand() % 225, 30 + rand() % 225, 30 + rand() % 225); - b.state = 0; - - const auto& children = item->items(1); - b.children_begin = static_cast(children.size()); - - fillTestChildren(item, max_depth, 1, x, first_level_children_count, total_items); - - const auto& last = children.back(); - b.setPos(x, last.right() - x); - b.totalHeight = GRAPHICS_ROW_SIZE_FULL + last.totalHeight; - - x += b.width() * 1.2; - - ++total_items; - } - - const auto h = item->getItem(0, 0).totalHeight; - item->setBoundingRect(0, 0, x, h); - - m_items.push_back(item); - scene()->addItem(item); - - if (maxX < x) - { - maxX = x; - longestItem = item; - } - } - - printf("TOTAL ITEMS = %u\n", total_items); - - // Calculate scene rect - auto item = thread_items.back(); - scene()->setSceneRect(0, 0, maxX, item->y() + item->getItem(0, 0).totalHeight); - - // Reset necessary values - m_offset = 0; - updateVisibleSceneRect(); - setScrollbar(m_pScrollbar); - - if (longestItem != nullptr) - { - m_pScrollbar->setMinimapFrom(0, longestItem->items(0)); - EASY_GLOBALS.selected_thread = 0; - emitEASY_GLOBALS.events.selectedThreadChanged(0); - } - - // Create new chronometer item (previous item was destroyed by scene on scene()->clear()). - // It will be shown on mouse right button click. - m_chronometerItemAux = createChronometer(false); - m_chronometerItem = createChronometer(true); - - bgItem->setBoundingRect(scene()->sceneRect()); - auto indicator = new EasyTimelineIndicatorItem(); - indicator->setBoundingRect(scene()->sceneRect()); - scene()->addItem(indicator); - - // Set necessary flags - m_bTest = true; - m_bEmpty = false; - - scaleTo(BASE_SCALE); -}*/ - -////////////////////////////////////////////////////////////////////////// - void EasyGraphicsView::clearSilent() { const QSignalBlocker blocker(this), sceneBlocker(scene()); // block all scene signals (otherwise clear() would be extremely slow!) @@ -1438,12 +1276,10 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr for (const auto& threadTree : _blocksTree) { const auto& t = threadTree.second; - auto timestart = blocksTree(t.children.front()).node->begin(); -#ifdef EASY_STORE_CSWITCH_SEPARATELY + auto timestart = blocksTree(t.children.front()).node->begin(); if (!t.sync.empty()) timestart = ::std::min(timestart, blocksTree(t.sync.front()).node->begin()); -#endif const auto timefinish = blocksTree(t.children.back()).node->end(); @@ -1704,9 +1540,10 @@ void EasyGraphicsView::updateTimelineStep(qreal _windowWidth) m_timelineStep = microseconds2units(m_timelineStep); } -void EasyGraphicsView::updateScene() +void EasyGraphicsView::repaintScene() { scene()->update(m_visibleSceneRect); + emit sceneUpdated(); } ////////////////////////////////////////////////////////////////////////// @@ -1728,7 +1565,7 @@ void EasyGraphicsView::scaleTo(qreal _scale) m_pScrollbar->setSliderWidth(windowWidth); updateTimelineStep(windowWidth); - updateScene(); + repaintScene(); } void EasyGraphicsView::wheelEvent(QWheelEvent* _event) @@ -1760,6 +1597,7 @@ void EasyGraphicsView::onWheel(qreal _mouseX, int _wheelDelta) const decltype(m_scale) scaleCoeff = _wheelDelta > 0 ? ::profiler_gui::SCALING_COEFFICIENT : ::profiler_gui::SCALING_COEFFICIENT_INV; // Remember current mouse position + _mouseX = clamp(0., _mouseX, scene()->width()); const auto mousePosition = m_offset + _mouseX / m_scale; // have to limit scale because of Qt's QPainter feature: it doesn't draw text @@ -1782,7 +1620,7 @@ void EasyGraphicsView::onWheel(qreal _mouseX, int _wheelDelta) updateVisibleSceneRect(); // Update scene rect updateTimelineStep(windowWidth); - updateScene(); // repaint scene + repaintScene(); // repaint scene } ////////////////////////////////////////////////////////////////////////// @@ -1828,6 +1666,7 @@ void EasyGraphicsView::mouseDoubleClickEvent(QMouseEvent* _event) m_chronometerItemAux->setLeftRight(mouseX, mouseX); m_chronometerItemAux->setReverse(false); m_chronometerItemAux->hide(); + emit sceneUpdated(); } _event->accept(); @@ -1843,6 +1682,7 @@ void EasyGraphicsView::mouseReleaseEvent(QMouseEvent* _event) return; } + bool chronoHidden = false; bool changedSelection = false, changedSelectedItem = false; if (m_mouseButtons & Qt::RightButton) { @@ -1883,6 +1723,7 @@ void EasyGraphicsView::mouseReleaseEvent(QMouseEvent* _event) if (m_chronometerItemAux->isVisible() && m_chronometerItemAux->width() < 1e-6) { + chronoHidden = true; m_chronometerItemAux->hide(); } else if (m_chronometerItem->isVisible() && m_chronometerItem->hoverIndicator()) @@ -1948,7 +1789,11 @@ void EasyGraphicsView::mouseReleaseEvent(QMouseEvent* _event) emit EASY_GLOBALS.events.selectedBlockChanged(EASY_GLOBALS.selected_block); m_bUpdatingRect = false; - updateScene(); + repaintScene(); + } + else if (chronoHidden) + { + emit sceneUpdated(); } } @@ -2011,7 +1856,7 @@ void EasyGraphicsView::mouseMoveEvent(QMouseEvent* _event) auto mouseScenePos = mapToScene(m_mousePressPos); mouseScenePos.setX(m_offset + mouseScenePos.x() / m_scale); - const auto x = clamp(0., mouseScenePos.x(), sceneRect().width()); + const auto x = clamp(0., mouseScenePos.x(), scene()->width()); if (m_mouseButtons & Qt::RightButton) { @@ -2067,7 +1912,7 @@ void EasyGraphicsView::mouseMoveEvent(QMouseEvent* _event) if (needUpdate) { - updateScene(); // repaint scene + repaintScene(); // repaint scene } _event->accept(); @@ -2075,11 +1920,77 @@ void EasyGraphicsView::mouseMoveEvent(QMouseEvent* _event) ////////////////////////////////////////////////////////////////////////// +void EasyGraphicsView::keyPressEvent(QKeyEvent* _event) +{ + static const int KeyStep = 100; + + const int key = _event->key(); + + switch (key) + { + case Qt::Key_Right: + case Qt::Key_6: + { + m_pScrollbar->setValue(m_pScrollbar->value() + KeyStep / m_scale); + break; + } + + case Qt::Key_Left: + case Qt::Key_4: + { + m_pScrollbar->setValue(m_pScrollbar->value() - KeyStep / m_scale); + break; + } + + case Qt::Key_Up: + case Qt::Key_8: + { + auto vbar = verticalScrollBar(); + vbar->setValue(vbar->value() - KeyStep); + break; + } + + case Qt::Key_Down: + case Qt::Key_2: + { + auto vbar = verticalScrollBar(); + vbar->setValue(vbar->value() + KeyStep); + break; + } + + case Qt::Key_Plus: + case Qt::Key_Equal: + { + onWheel(mapToScene(mapFromGlobal(QCursor::pos())).x(), KeyStep); + break; + } + + case Qt::Key_Minus: + { + onWheel(mapToScene(mapFromGlobal(QCursor::pos())).x(), -KeyStep); + break; + } + } + + m_keys.insert(key); + _event->accept(); +} + +void EasyGraphicsView::keyReleaseEvent(QKeyEvent* _event) +{ + const int key = _event->key(); + + m_keys.erase(key); + _event->accept(); +} + +////////////////////////////////////////////////////////////////////////// + void EasyGraphicsView::resizeEvent(QResizeEvent* _event) { QGraphicsView::resizeEvent(_event); updateVisibleSceneRect(); // Update scene visible rect only once - updateScene(); // repaint scene + repaintScene(); // repaint scene } ////////////////////////////////////////////////////////////////////////// @@ -2102,8 +2013,8 @@ void EasyGraphicsView::initMode() connect(globalSignals, &::profiler_gui::EasyGlobalSignals::selectedThreadChanged, this, &This::onSelectedThreadChange); connect(globalSignals, &::profiler_gui::EasyGlobalSignals::selectedBlockChanged, this, &This::onSelectedBlockChange); connect(globalSignals, &::profiler_gui::EasyGlobalSignals::itemsExpandStateChanged, this, &This::onItemsEspandStateChange); - connect(globalSignals, &::profiler_gui::EasyGlobalSignals::drawBordersChanged, this, &This::updateScene); - connect(globalSignals, &::profiler_gui::EasyGlobalSignals::chronoPositionChanged, this, &This::updateScene); + connect(globalSignals, &::profiler_gui::EasyGlobalSignals::drawBordersChanged, this, &This::repaintScene); + connect(globalSignals, &::profiler_gui::EasyGlobalSignals::chronoPositionChanged, this, &This::repaintScene); } ////////////////////////////////////////////////////////////////////////// @@ -2122,7 +2033,7 @@ void EasyGraphicsView::onGraphicsScrollbarValueChange(qreal _value) if (!m_bUpdatingRect) { updateVisibleSceneRect(); - updateScene(); + repaintScene(); } } } @@ -2153,7 +2064,7 @@ void EasyGraphicsView::onFlickerTimeout() // because if scrollbar does not emit valueChanged signal then viewport does not move updateVisibleSceneRect(); // Update scene visible rect only once - updateScene(); // repaint scene + repaintScene(); // repaint scene m_flickerSpeedX -= absmin(sign(m_flickerSpeedX), m_flickerSpeedX); m_flickerSpeedY -= absmin(sign(m_flickerSpeedY), m_flickerSpeedY); @@ -2186,13 +2097,13 @@ void EasyGraphicsView::onSelectedThreadChange(::profiler::thread_id_t _id) if (item->threadId() == _id) { m_pScrollbar->setMinimapFrom(_id, item->items(0)); - updateScene(); + repaintScene(); return; } } m_pScrollbar->setMinimapFrom(0, nullptr); - updateScene(); + repaintScene(); } ////////////////////////////////////////////////////////////////////////// @@ -2218,7 +2129,7 @@ void EasyGraphicsView::onSelectedBlockChange(unsigned int _block_index) } updateVisibleSceneRect(); - updateScene(); + repaintScene(); } } @@ -2228,7 +2139,7 @@ void EasyGraphicsView::onItemsEspandStateChange() { if (!m_bUpdatingRect) { - updateScene(); + repaintScene(); } } @@ -2238,7 +2149,7 @@ EasyGraphicsViewWidget::EasyGraphicsViewWidget(QWidget* _parent) : QWidget(_parent) , m_scrollbar(new EasyGraphicsScrollbar(this)) , m_view(new EasyGraphicsView(this)) - , m_threadNamesWidget(new EasyThreadNamesWidget(m_view, this)) + , m_threadNamesWidget(new EasyThreadNamesWidget(m_view, m_scrollbar->height(), this)) { initWidget(); } @@ -2247,7 +2158,7 @@ void EasyGraphicsViewWidget::initWidget() { auto lay = new QGridLayout(this); lay->setContentsMargins(1, 0, 1, 0); - lay->addWidget(m_threadNamesWidget, 0, 0); + lay->addWidget(m_threadNamesWidget, 0, 0, 2, 1); lay->setSpacing(1); lay->addWidget(m_view, 0, 1); lay->setSpacing(1); @@ -2293,18 +2204,19 @@ void EasyThreadNameItem::paint(QPainter* _painter, const QStyleOptionGraphicsIte return; const auto visibleSceneRect = view->visibleSceneRect(); - const auto h = visibleSceneRect.height() + TIMELINE_ROW_SIZE; + const auto h = visibleSceneRect.height() + TIMELINE_ROW_SIZE - 2; const auto w = scene()->sceneRect().width(); - static const auto OVERLAP = THREADS_ROW_SPACING >> 1; + static const uint16_t OVERLAP = THREADS_ROW_SPACING >> 1; static const QBrush brushes[2] = {QColor::fromRgb(BACKGROUND_1), QColor::fromRgb(BACKGROUND_2)}; int i = -1; QRectF rect; _painter->resetTransform(); - _painter->setFont(BG_FONT); + // Draw thread names + _painter->setFont(BG_FONT); for (auto item : items) { ++i; @@ -2322,9 +2234,16 @@ void EasyThreadNameItem::paint(QPainter* _painter, const QStyleOptionGraphicsIte else _painter->setBrush(brushes[i & 1]); - const auto dh = rect.bottom() - h; - if (dh > 0) hgt -= dh; - if (top < 0) { hgt += top; top = 0; } + if (top < 0) + { + hgt += top; + top = 0; + } + + const auto dh = top + hgt - h; + if (dh > 0) + hgt -= dh; + rect.setRect(0, top, w, hgt); _painter->setPen(Qt::NoPen); @@ -2334,6 +2253,37 @@ void EasyThreadNameItem::paint(QPainter* _painter, const QStyleOptionGraphicsIte _painter->setPen(QColor::fromRgb(::profiler::colors::Dark)); _painter->drawText(rect, Qt::AlignRight | Qt::AlignVCenter, item->threadName()); } + + // Draw separator between thread names area and information area + _painter->setPen(Qt::darkGray); + _painter->drawLine(QLineF(0, h, w, h)); + _painter->drawLine(QLineF(0, h + 2, w, h + 2)); + + // Draw information + _painter->setFont(CHRONOMETER_FONT); + QFontMetricsF fm(CHRONOMETER_FONT); + const qreal time1 = view->chronoTime(); + const qreal time2 = view->chronoTimeAux(); + + auto y = h + 2; + + auto drawTimeText = [&rect, &w, &y, &fm, &_painter](qreal time, QRgb color) + { + if (time > 0) + { + const QString text = ::profiler_gui::timeStringReal(time); // Displayed text + const auto textRect = fm.boundingRect(text); // Calculate displayed text boundingRect + rect.setRect(0, y, w, textRect.height()); + + _painter->setPen(color); + _painter->drawText(rect, Qt::AlignCenter, text); + + y += textRect.height(); + } + }; + + drawTimeText(time1, CHRONOMETER_COLOR.rgb() & 0x00ffffff); + drawTimeText(time2, CHRONOMETER_COLOR2.rgb() & 0x00ffffff); } void EasyThreadNameItem::setBoundingRect(const QRectF& _rect) @@ -2343,8 +2293,9 @@ void EasyThreadNameItem::setBoundingRect(const QRectF& _rect) ////////////////////////////////////////////////////////////////////////// -EasyThreadNamesWidget::EasyThreadNamesWidget(EasyGraphicsView* _view, QWidget* _parent) : Parent(_parent) +EasyThreadNamesWidget::EasyThreadNamesWidget(EasyGraphicsView* _view, int _additionalHeight, QWidget* _parent) : Parent(_parent) , m_view(_view) + , m_additionalHeight(_additionalHeight + 1) { setScene(new QGraphicsScene(this)); @@ -2356,8 +2307,9 @@ EasyThreadNamesWidget::EasyThreadNamesWidget(EasyGraphicsView* _view, QWidget* _ connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedThreadChanged, this, &This::onSelectedThreadChange); connect(m_view, &EasyGraphicsView::treeChanged, this, &This::onTreeChange); + connect(m_view, &EasyGraphicsView::sceneUpdated, this, &This::repaintScene); connect(m_view->verticalScrollBar(), &QScrollBar::valueChanged, verticalScrollBar(), &QScrollBar::setValue); - connect(m_view->verticalScrollBar(), &QScrollBar::rangeChanged, verticalScrollBar(), &QScrollBar::setRange); + connect(m_view->verticalScrollBar(), &QScrollBar::rangeChanged, this, &This::setVerticalScrollbarRange); } EasyThreadNamesWidget::~EasyThreadNamesWidget() @@ -2365,6 +2317,11 @@ EasyThreadNamesWidget::~EasyThreadNamesWidget() } +void EasyThreadNamesWidget::setVerticalScrollbarRange(int _minValue, int _maxValue) +{ + verticalScrollBar()->setRange(_minValue, _maxValue + m_additionalHeight); +} + void EasyThreadNamesWidget::onTreeChange() { QSignalBlocker b(this); @@ -2379,12 +2336,12 @@ void EasyThreadNamesWidget::onTreeChange() auto vbar = verticalScrollBar(); auto viewBar = m_view->verticalScrollBar(); - vbar->setRange(viewBar->minimum(), viewBar->maximum()); + setVerticalScrollbarRange(viewBar->minimum(), viewBar->maximum()); vbar->setSingleStep(viewBar->singleStep()); vbar->setPageStep(viewBar->pageStep()); auto r = m_view->sceneRect(); - setSceneRect(0, r.top(), maxLength, r.height()); + setSceneRect(0, r.top(), maxLength, r.height() + m_additionalHeight); auto item = new EasyThreadNameItem(); item->setPos(0, 0); @@ -2401,6 +2358,11 @@ void EasyThreadNamesWidget::onSelectedThreadChange(::profiler::thread_id_t) scene()->update(); } +void EasyThreadNamesWidget::repaintScene() +{ + scene()->update(); +} + void EasyThreadNamesWidget::mousePressEvent(QMouseEvent* _event) { QMouseEvent e(_event->type(), _event->pos() - QPointF(sceneRect().width(), 0), _event->button(), _event->buttons() & ~Qt::RightButton, _event->modifiers()); @@ -2450,6 +2412,16 @@ void EasyThreadNamesWidget::mouseMoveEvent(QMouseEvent* _event) _event->accept(); } +void EasyThreadNamesWidget::keyPressEvent(QKeyEvent* _event) +{ + m_view->keyPressEvent(_event); +} + +void EasyThreadNamesWidget::keyReleaseEvent(QKeyEvent* _event) +{ + m_view->keyReleaseEvent(_event); +} + ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// diff --git a/profiler_gui/blocks_graphics_view.h b/profiler_gui/blocks_graphics_view.h index 3344976..3ff4a51 100644 --- a/profiler_gui/blocks_graphics_view.h +++ b/profiler_gui/blocks_graphics_view.h @@ -44,6 +44,7 @@ #include #include #include +#include #include "graphics_scrollbar.h" #include "profiler/reader.h" #include "common_types.h" @@ -278,26 +279,28 @@ private: typedef EasyGraphicsView This; typedef ::std::vector Items; + typedef ::std::unordered_set::hasher_t> Keys; - Items m_items; ///< Array of all EasyGraphicsItem items - ::profiler_gui::TreeBlocks m_selectedBlocks; ///< Array of items which were selected by selection zone (EasyChronometerItem) - QTimer m_flickerTimer; ///< Timer for flicking behavior - QRectF m_visibleSceneRect; ///< Visible scene rectangle - ::profiler::timestamp_t m_beginTime; ///< Begin time of profiler session. Used to reduce values of all begin and end times of profiler blocks. - qreal m_scale; ///< Current scale - qreal m_offset; ///< Have to use manual offset for all scene content instead of using scrollbars because QScrollBar::value is 32-bit integer :( - qreal m_timelineStep; ///< - QPoint m_mousePressPos; ///< Last mouse global position (used by mousePressEvent and mouseMoveEvent) - QPoint m_mouseMovePath; ///< Mouse move path between press and release of any button - Qt::MouseButtons m_mouseButtons; ///< Pressed mouse buttons - EasyGraphicsScrollbar* m_pScrollbar; ///< Pointer to the graphics scrollbar widget - EasyChronometerItem* m_chronometerItem; ///< Pointer to the EasyChronometerItem which is displayed when you press right mouse button and move mouse left or right. This item is used to select blocks to display in tree widget. - EasyChronometerItem* m_chronometerItemAux; ///< Pointer to the EasyChronometerItem which is displayed when you double click left mouse button and move mouse left or right. This item is used only to measure time. - int m_flickerSpeedX; ///< Current flicking speed x - int m_flickerSpeedY; ///< Current flicking speed y - bool m_bDoubleClick; ///< Is mouse buttons double clicked - bool m_bUpdatingRect; ///< Stub flag which is used to avoid excess calculations on some scene update (flicking, scaling and so on) - bool m_bEmpty; ///< Indicates whether scene is empty and has no items + Items m_items; ///< Array of all EasyGraphicsItem items + Keys m_keys; ///< Pressed keys + ::profiler_gui::TreeBlocks m_selectedBlocks; ///< Array of items which were selected by selection zone (EasyChronometerItem) + QTimer m_flickerTimer; ///< Timer for flicking behavior + QRectF m_visibleSceneRect; ///< Visible scene rectangle + ::profiler::timestamp_t m_beginTime; ///< Begin time of profiler session. Used to reduce values of all begin and end times of profiler blocks. + qreal m_scale; ///< Current scale + qreal m_offset; ///< Have to use manual offset for all scene content instead of using scrollbars because QScrollBar::value is 32-bit integer :( + qreal m_timelineStep; ///< + QPoint m_mousePressPos; ///< Last mouse global position (used by mousePressEvent and mouseMoveEvent) + QPoint m_mouseMovePath; ///< Mouse move path between press and release of any button + Qt::MouseButtons m_mouseButtons; ///< Pressed mouse buttons + EasyGraphicsScrollbar* m_pScrollbar; ///< Pointer to the graphics scrollbar widget + EasyChronometerItem* m_chronometerItem; ///< Pointer to the EasyChronometerItem which is displayed when you press right mouse button and move mouse left or right. This item is used to select blocks to display in tree widget. + EasyChronometerItem* m_chronometerItemAux; ///< Pointer to the EasyChronometerItem which is displayed when you double click left mouse button and move mouse left or right. This item is used only to measure time. + int m_flickerSpeedX; ///< Current flicking speed x + int m_flickerSpeedY; ///< Current flicking speed y + bool m_bDoubleClick; ///< Is mouse buttons double clicked + bool m_bUpdatingRect; ///< Stub flag which is used to avoid excess calculations on some scene update (flicking, scaling and so on) + bool m_bEmpty; ///< Indicates whether scene is empty and has no items public: @@ -311,6 +314,8 @@ public: void mouseDoubleClickEvent(QMouseEvent* _event) override; void mouseReleaseEvent(QMouseEvent* _event) override; void mouseMoveEvent(QMouseEvent* _event) override; + void keyPressEvent(QKeyEvent* _event) override; + void keyReleaseEvent(QKeyEvent* _event) override; void resizeEvent(QResizeEvent* _event) override; public: @@ -328,6 +333,7 @@ signals: // Signals + void sceneUpdated(); void treeChanged(); void intervalChanged(const ::profiler_gui::TreeBlocks& _blocks, ::profiler::timestamp_t _session_begin_time, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict); @@ -348,7 +354,7 @@ private slots: // Private Slots - void updateScene(); + void repaintScene(); void onGraphicsScrollbarWheel(qreal _mouseX, int _wheelDelta); void onScrollbarValueChange(int); void onGraphicsScrollbarValueChange(qreal); @@ -381,6 +387,16 @@ public: return m_timelineStep; } + inline qreal chronoTime() const + { + return m_chronometerItem->width(); + } + + inline qreal chronoTimeAux() const + { + return m_chronometerItemAux->width(); + } + //private: // Private inline methods @@ -433,27 +449,32 @@ private: typedef QGraphicsView Parent; typedef EasyThreadNamesWidget This; - EasyGraphicsView* m_view; + EasyGraphicsView* m_view; + const int m_additionalHeight; public: - explicit EasyThreadNamesWidget(EasyGraphicsView* _view, QWidget* _parent = nullptr); - virtual ~EasyThreadNamesWidget(); + explicit EasyThreadNamesWidget(EasyGraphicsView* _view, int _additionalHeight, QWidget* _parent = nullptr); + virtual ~EasyThreadNamesWidget(); - void mousePressEvent(QMouseEvent* _event) override; - void mouseDoubleClickEvent(QMouseEvent* _event) override; - void mouseReleaseEvent(QMouseEvent* _event) override; - void mouseMoveEvent(QMouseEvent* _event) override; + void mousePressEvent(QMouseEvent* _event) override; + void mouseDoubleClickEvent(QMouseEvent* _event) override; + void mouseReleaseEvent(QMouseEvent* _event) override; + void mouseMoveEvent(QMouseEvent* _event) override; + void keyPressEvent(QKeyEvent* _event) override; + void keyReleaseEvent(QKeyEvent* _event) override; - const EasyGraphicsView* view() const - { - return m_view; - } + const EasyGraphicsView* view() const + { + return m_view; + } private slots: - void onTreeChange(); - void onSelectedThreadChange(::profiler::thread_id_t _id); + void setVerticalScrollbarRange(int _minValue, int _maxValue); + void onTreeChange(); + void onSelectedThreadChange(::profiler::thread_id_t _id); + void repaintScene(); }; // END of class EasyThreadNamesWidget. diff --git a/profiler_gui/common_types.h b/profiler_gui/common_types.h index 55a9bef..976761e 100644 --- a/profiler_gui/common_types.h +++ b/profiler_gui/common_types.h @@ -119,6 +119,12 @@ inline bool isLightColor(::profiler::color_t _color) return sum < 76.5 || ((_color & 0xff000000) >> 24) < 0x80; } +inline bool isLightColor(::profiler::color_t _color, qreal _maxSum) +{ + const auto sum = 255. - (((_color & 0x00ff0000) >> 16) * 0.299 + ((_color & 0x0000ff00) >> 8) * 0.587 + (_color & 0x000000ff) * 0.114); + return sum < _maxSum || ((_color & 0xff000000) >> 24) < 0x80; +} + inline ::profiler::color_t textColorForRgb(::profiler::color_t _color) { return isLightColor(_color) ? ::profiler::colors::Dark : ::profiler::colors::CreamWhite; diff --git a/profiler_gui/globals.h b/profiler_gui/globals.h index 30357b9..61cbc47 100644 --- a/profiler_gui/globals.h +++ b/profiler_gui/globals.h @@ -46,7 +46,7 @@ namespace profiler_gui { const QString ORGANAZATION_NAME = "EasyProfiler"; const QString APPLICATION_NAME = "Easy profiler gui application"; - const QColor CHRONOMETER_COLOR = QColor::fromRgba(0x402020c0); + const QColor CHRONOMETER_COLOR = QColor::fromRgba(0x40000000 | (::profiler::colors::RichBlue & 0x00ffffff));// 0x402020c0); const QRgb SELECTED_THREAD_BACKGROUND = 0x00e0e060; const QRgb SELECTED_THREAD_FOREGROUND = 0x00ffffff - SELECTED_THREAD_BACKGROUND; From 9294ddb61b54d836d457f5636e7dfe5262cd4474 Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Fri, 9 Sep 2016 00:09:47 +0300 Subject: [PATCH 04/25] (profiler_core) Prepare for blocks enable/disable; (profiler_core) Changed order of read/write of context switches as a preparation work to statistics gathering taking into account context switches duration. --- include/profiler/profiler.h | 50 ++++++----- include/profiler/reader.h | 9 -- include/profiler/serialized_block.h | 2 + src/block.cpp | 15 ++-- src/event_trace_win.cpp | 4 +- src/profile_manager.cpp | 120 ++++++++++++-------------- src/profile_manager.h | 16 ++-- src/reader.cpp | 129 +++++++++++++--------------- 8 files changed, 169 insertions(+), 176 deletions(-) diff --git a/include/profiler/profiler.h b/include/profiler/profiler.h index b9da0d5..49491bf 100644 --- a/include/profiler/profiler.h +++ b/include/profiler/profiler.h @@ -104,9 +104,9 @@ Block will be automatically completed by destructor. \ingroup profiler */ #define EASY_BLOCK(name, ...)\ - static const ::profiler::block_id_t EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(EASY_COMPILETIME_NAME(name), __FILE__, __LINE__,\ + static const ::profiler::BaseBlockDescriptor& EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(EASY_COMPILETIME_NAME(name), __FILE__, __LINE__,\ ::profiler::BLOCK_TYPE_BLOCK , ## __VA_ARGS__);\ - ::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(::profiler::BLOCK_TYPE_BLOCK, EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name));\ + ::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name));\ ::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable /** Macro of beginning of block with function name and custom color. @@ -129,9 +129,9 @@ Name of the block automatically created with function name. \ingroup profiler */ #define EASY_FUNCTION(...)\ - static const ::profiler::block_id_t EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(__func__, __FILE__, __LINE__,\ + static const ::profiler::BaseBlockDescriptor& EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(__func__, __FILE__, __LINE__,\ ::profiler::BLOCK_TYPE_BLOCK , ## __VA_ARGS__);\ - ::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(::profiler::BLOCK_TYPE_BLOCK, EASY_UNIQUE_DESC(__LINE__), "");\ + ::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(EASY_UNIQUE_DESC(__LINE__), "");\ ::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable /** Macro of completion of last nearest open block. @@ -169,10 +169,9 @@ will end previously opened EASY_BLOCK or EASY_FUNCTION. \ingroup profiler */ #define EASY_EVENT(name, ...)\ - static const ::profiler::block_id_t EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(EASY_COMPILETIME_NAME(name), __FILE__, __LINE__,\ - ::profiler::BLOCK_TYPE_EVENT , ## __VA_ARGS__);\ - ::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(::profiler::BLOCK_TYPE_EVENT, EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name));\ - ::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable + static const ::profiler::BaseBlockDescriptor& EASY_UNIQUE_DESC(__LINE__) = \ + ::profiler::registerDescription(EASY_COMPILETIME_NAME(name), __FILE__, __LINE__, ::profiler::BLOCK_TYPE_EVENT , ## __VA_ARGS__);\ + ::profiler::storeBlock(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name)); /** Macro enabling profiler \ingroup profiler @@ -229,11 +228,11 @@ This is only for user comfort. There is no difference for EasyProfiler GUI betwe #endif class ProfileManager; -class ThreadStorage; namespace profiler { class Block; + class BaseBlockDescriptor; typedef uint64_t timestamp_t; typedef uint32_t thread_id_t; @@ -251,42 +250,51 @@ namespace profiler { typedef BlockType block_type_t; extern "C" { - PROFILER_API block_id_t registerDescription(const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color = ::profiler::colors::Default); + PROFILER_API const BaseBlockDescriptor& registerDescription(const char* _compiletimeName, const char* _filename, int _line, block_type_t _block_type, color_t _color = ::profiler::colors::Default); + PROFILER_API void storeBlock(const BaseBlockDescriptor& _desc, const char* _runtimeName); PROFILER_API void beginBlock(Block& _block); PROFILER_API void endBlock(); - PROFILER_API void setEnabled(bool isEnable); - PROFILER_API uint32_t dumpBlocksToFile(const char* filename); - PROFILER_API const char* setThreadName(const char* name, const char* filename, const char* _funcname, int line); - PROFILER_API void setContextSwitchLogFilename(const char* name); + PROFILER_API void setEnabled(bool _isEnable); + PROFILER_API uint32_t dumpBlocksToFile(const char* _filename); + PROFILER_API const char* setThreadName(const char* _name, const char* _filename, const char* _funcname, int _line); + PROFILER_API void setContextSwitchLogFilename(const char* _name); PROFILER_API const char* getContextSwitchLogFilename(); } #pragma pack(push,1) class PROFILER_API BaseBlockDescriptor { + friend ::ProfileManager; + 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 - BaseBlockDescriptor(int _line, block_type_t _block_type, color_t _color); + BaseBlockDescriptor(block_id_t _id, int _line, block_type_t _block_type, color_t _color); public: + inline block_id_t id() const { return m_id; } inline int line() const { return m_line; } - inline block_type_t type() const { return m_type; } inline color_t color() const { return m_color; } + inline block_type_t type() const { return m_type; } + inline bool enabled() const { return m_enabled; } }; class PROFILER_API BlockDescriptor final : public BaseBlockDescriptor { + 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 public: - BlockDescriptor(uint64_t& _used_mem, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color); + BlockDescriptor(uint64_t& _used_mem, block_id_t _id, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color); inline const char* name() const { return m_name; } inline const char* file() const { return m_filename; } @@ -325,18 +333,20 @@ namespace profiler { friend ::ProfileManager; const char* m_name; + bool m_enabled; private: void finish(); void finish(timestamp_t _end_time); - inline bool isFinished() const { return m_end >= m_begin; } + inline bool finished() const { return m_end >= m_begin; } + inline bool enabled() const { return m_enabled; } public: Block(Block&& that); - Block(block_type_t _block_type, block_id_t _id, const char* _name); - Block(timestamp_t _begin_time, block_type_t _block_type, block_id_t _id, const char* _name); + Block(const BaseBlockDescriptor& _desc, const char* _runtimeName); + Block(timestamp_t _begin_time, block_id_t _id, bool _enabled, const char* _runtimeName); ~Block(); inline const char* name() const { return m_name; } diff --git a/include/profiler/reader.h b/include/profiler/reader.h index 3bf0e9e..0dc38cd 100644 --- a/include/profiler/reader.h +++ b/include/profiler/reader.h @@ -167,9 +167,6 @@ namespace profiler { ////////////////////////////////////////////////////////////////////////// -#define EASY_STORE_CSWITCH_SEPARATELY -//#undef EASY_STORE_CSWITCH_SEPARATELY - class BlocksTreeRoot final { typedef BlocksTreeRoot This; @@ -177,9 +174,7 @@ namespace profiler { public: BlocksTree::children_t children; -#ifdef EASY_STORE_CSWITCH_SEPARATELY BlocksTree::children_t sync; -#endif const char* thread_name; ::profiler::thread_id_t thread_id; uint16_t depth; @@ -190,9 +185,7 @@ namespace profiler { BlocksTreeRoot(This&& that) : children(::std::move(that.children)) -#ifdef EASY_STORE_CSWITCH_SEPARATELY , sync(::std::move(that.sync)) -#endif , thread_name(that.thread_name) , thread_id(that.thread_id) , depth(that.depth) @@ -202,9 +195,7 @@ namespace profiler { This& operator = (This&& that) { children = ::std::move(that.children); -#ifdef EASY_STORE_CSWITCH_SEPARATELY sync = ::std::move(that.sync); -#endif thread_name = that.thread_name; thread_id = that.thread_id; depth = that.depth; diff --git a/include/profiler/serialized_block.h b/include/profiler/serialized_block.h index c8dd1e8..01f45a3 100644 --- a/include/profiler/serialized_block.h +++ b/include/profiler/serialized_block.h @@ -21,6 +21,8 @@ along with this program.If not, see . #include "profiler/profiler.h" +class ThreadStorage; + namespace profiler { ////////////////////////////////////////////////////////////////////////// diff --git a/src/block.cpp b/src/block.cpp index 1fca4cc..48b2ce3 100644 --- a/src/block.cpp +++ b/src/block.cpp @@ -67,19 +67,16 @@ Block::Block(Block&& that) m_end = that.m_end; } -Block::Block(block_type_t _block_type, block_id_t _descriptor_id, const char* _name) - : Block(getCurrentTime(), _block_type, _descriptor_id, _name) +Block::Block(const BaseBlockDescriptor& _descriptor, const char* _runtimeName) + : Block(getCurrentTime(), _descriptor.id(), _descriptor.enabled(), _runtimeName) { } -Block::Block(timestamp_t _begin_time, block_type_t _block_type, block_id_t _descriptor_id, const char* _name) +Block::Block(timestamp_t _begin_time, block_id_t _descriptor_id, bool _enabled, const char* _runtimeName) : BaseBlockData(_begin_time, _descriptor_id) - , m_name(_name) + , m_name(_runtimeName) + , m_enabled(_enabled) { - if (static_cast(_block_type) < BLOCK_TYPE_BLOCK) - { - m_end = m_begin; - } } void Block::finish() @@ -94,6 +91,6 @@ void Block::finish(timestamp_t _end_time) Block::~Block() { - if (!isFinished()) + if (!finished()) ::profiler::endBlock(); } diff --git a/src/event_trace_win.cpp b/src/event_trace_win.cpp index 0b947d3..d60ed94 100644 --- a/src/event_trace_win.cpp +++ b/src/event_trace_win.cpp @@ -48,8 +48,8 @@ namespace profiler { auto _contextSwitchEvent = reinterpret_cast(_traceEvent->UserData); const auto time = static_cast<::profiler::timestamp_t>(_traceEvent->EventHeader.TimeStamp.QuadPart); - static const auto desc = MANAGER.addBlockDescriptor("OS.ContextSwitch", __FILE__, __LINE__, ::profiler::BLOCK_TYPE_CONTEXT_SWITCH, ::profiler::colors::White); - MANAGER.beginContextSwitch(_contextSwitchEvent->OldThreadId, time, desc); + static const auto& desc = MANAGER.addBlockDescriptor("OS.ContextSwitch", __FILE__, __LINE__, ::profiler::BLOCK_TYPE_CONTEXT_SWITCH, ::profiler::colors::White); + MANAGER.beginContextSwitch(_contextSwitchEvent->OldThreadId, time, desc.id()); MANAGER.endContextSwitch(_contextSwitchEvent->NewThreadId, time); } diff --git a/src/profile_manager.cpp b/src/profile_manager.cpp index 6f9ba32..118390c 100644 --- a/src/profile_manager.cpp +++ b/src/profile_manager.cpp @@ -46,7 +46,7 @@ extern decltype(LARGE_INTEGER::QuadPart) CPU_FREQUENCY; extern "C" { - PROFILER_API block_id_t registerDescription(const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color) + PROFILER_API const BaseBlockDescriptor& registerDescription(const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color) { return MANAGER.addBlockDescriptor(_name, _filename, _line, _block_type, _color); } @@ -67,6 +67,11 @@ extern "C" { #endif } + PROFILER_API void storeBlock(const BaseBlockDescriptor& _desc, const char* _runtimeName) + { + MANAGER.storeBlock(_desc, _runtimeName); + } + PROFILER_API void beginBlock(Block& _block) { MANAGER.beginBlock(_block); @@ -104,16 +109,18 @@ SerializedBlock::SerializedBlock(const Block& block, uint16_t name_length) ////////////////////////////////////////////////////////////////////////// -BaseBlockDescriptor::BaseBlockDescriptor(int _line, block_type_t _block_type, color_t _color) - : m_line(_line) +BaseBlockDescriptor::BaseBlockDescriptor(block_id_t _id, 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(true) { } -BlockDescriptor::BlockDescriptor(uint64_t& _used_mem, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color) - : BaseBlockDescriptor(_line, _block_type, _color) +BlockDescriptor::BlockDescriptor(uint64_t& _used_mem, block_id_t _id, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color) + : BaseBlockDescriptor(_id, _line, _block_type, _color) , m_name(_name) , m_filename(_filename) { @@ -176,6 +183,10 @@ ProfileManager::ProfileManager() ProfileManager::~ProfileManager() { //dumpBlocksToFile("test.prof"); + for (auto desc : m_descriptors) + { + delete desc; + } } ProfileManager& ProfileManager::instance() @@ -186,6 +197,20 @@ ProfileManager& ProfileManager::instance() return m_profileManager; } +void ProfileManager::storeBlock(const profiler::BaseBlockDescriptor& _desc, const char* _runtimeName) +{ + if (!m_isEnabled || !_desc.enabled()) + return; + + profiler::Block b(_desc, _runtimeName); + b.finish(b.begin()); + + if (THREAD_STORAGE == nullptr) + THREAD_STORAGE = &threadStorage(getCurrentThreadId()); + + THREAD_STORAGE->storeBlock(b); +} + void ProfileManager::beginBlock(Block& _block) { if (!m_isEnabled) @@ -194,17 +219,14 @@ void ProfileManager::beginBlock(Block& _block) if (THREAD_STORAGE == nullptr) THREAD_STORAGE = &threadStorage(getCurrentThreadId()); - if (!_block.isFinished()) - THREAD_STORAGE->blocks.openedList.emplace(_block); - else - THREAD_STORAGE->storeBlock(_block); + THREAD_STORAGE->blocks.openedList.emplace(_block); } void ProfileManager::beginContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id) { auto ts = findThreadStorage(_thread_id); if (ts != nullptr) - ts->sync.openedList.emplace(_time, profiler::BLOCK_TYPE_CONTEXT_SWITCH, _id, ""); + ts->sync.openedList.emplace(_time, _id, true, ""); } void ProfileManager::storeContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id) @@ -212,9 +234,9 @@ void ProfileManager::storeContextSwitch(profiler::thread_id_t _thread_id, profil auto ts = findThreadStorage(_thread_id); if (ts != nullptr) { - profiler::Block lastBlock(_time, profiler::BLOCK_TYPE_CONTEXT_SWITCH, _id, ""); - lastBlock.finish(_time); - ts->storeCSwitch(lastBlock); + profiler::Block b(_time, _id, true, ""); + b.finish(_time); + ts->storeCSwitch(b); } } @@ -227,10 +249,13 @@ void ProfileManager::endBlock() return; Block& lastBlock = THREAD_STORAGE->blocks.openedList.top(); - if (!lastBlock.isFinished()) - lastBlock.finish(); + if (lastBlock.enabled()) + { + if (!lastBlock.finished()) + lastBlock.finish(); + THREAD_STORAGE->storeBlock(lastBlock); + } - THREAD_STORAGE->storeBlock(lastBlock); THREAD_STORAGE->blocks.openedList.pop(); } @@ -280,8 +305,6 @@ public: ////////////////////////////////////////////////////////////////////////// -#define STORE_CSWITCHES_SEPARATELY - uint32_t ProfileManager::dumpBlocksToFile(const char* filename) { const bool wasEnabled = m_isEnabled; @@ -329,17 +352,17 @@ uint32_t ProfileManager::dumpBlocksToFile(const char* filename) of.write(static_cast(m_descriptors.size())); of.write(m_usedMemorySize); - for (const auto& descriptor : m_descriptors) + for (const auto descriptor : m_descriptors) { - const auto name_size = static_cast(strlen(descriptor.name()) + 1); - const auto filename_size = static_cast(strlen(descriptor.file()) + 1); + const auto name_size = static_cast(strlen(descriptor->name()) + 1); + const auto filename_size = static_cast(strlen(descriptor->file()) + 1); const auto size = static_cast(sizeof(profiler::SerializedBlockDescriptor) + name_size + filename_size); of.write(size); - of.write(descriptor); + of.write(*descriptor); of.write(name_size); - of.write(descriptor.name(), name_size); - of.write(descriptor.file(), filename_size); + of.write(descriptor->name(), name_size); + of.write(descriptor->file(), filename_size); } for (auto& thread_storage : m_threads) @@ -347,47 +370,14 @@ uint32_t ProfileManager::dumpBlocksToFile(const char* filename) auto& t = thread_storage.second; of.write(thread_storage.first); -#ifdef STORE_CSWITCHES_SEPARATELY - of.write(static_cast(t.blocks.closedList.size())); -#else - of.write(static_cast(t.blocks.closedList.size()) + static_cast(t.sync.closedList.size())); - uint32_t i = 0; -#endif - for (auto b : t.blocks.closedList) - { -#ifndef STORE_CSWITCHES_SEPARATELY - if (i < t.sync.closedList.size()) - { - auto s = t.sync.closedList[i]; - if (s->end() <= b->end())// || s->begin() >= b->begin()) - //if (((s->end() <= b->end() && s->end() >= b->begin()) || (s->begin() >= b->begin() && s->begin() <= b->end()))) - { - if (s->m_begin < b->m_begin) - s->m_begin = b->m_begin; - if (s->m_end > b->m_end) - s->m_end = b->m_end; - of.writeBlock(s); - ++i; - } - } -#endif - - of.writeBlock(b); - } - -#ifdef STORE_CSWITCHES_SEPARATELY of.write(static_cast(t.sync.closedList.size())); for (auto b : t.sync.closedList) - { -#else - for (; i < t.sync.closedList.size(); ++i) - { - auto b = t.sync.closedList[i]; -#endif - of.writeBlock(b); - } + + of.write(static_cast(t.blocks.closedList.size())); + for (auto b : t.blocks.closedList) + of.writeBlock(b); t.clearClosed(); } @@ -407,8 +397,12 @@ const char* ProfileManager::setThreadName(const char* name, const char* filename if (!THREAD_STORAGE->named) { - const auto id = addBlockDescriptor(_funcname, filename, line, profiler::BLOCK_TYPE_THREAD_SIGN, profiler::colors::Black); - THREAD_STORAGE->storeBlock(profiler::Block(profiler::BLOCK_TYPE_THREAD_SIGN, id, name)); + const auto& desc = addBlockDescriptor(_funcname, filename, line, profiler::BLOCK_TYPE_THREAD_SIGN, profiler::colors::Black); + + profiler::Block b(desc, name); + b.finish(b.begin()); + + THREAD_STORAGE->storeBlock(b); THREAD_STORAGE->name = name; THREAD_STORAGE->named = true; } diff --git a/src/profile_manager.h b/src/profile_manager.h index 0259cbb..2b54726 100644 --- a/src/profile_manager.h +++ b/src/profile_manager.h @@ -118,6 +118,11 @@ struct BlocksList final m_stack.emplace_back(_block); } + inline void emplace(profiler::Block&& _block) { + //m_stack.emplace(_block); + m_stack.emplace_back(std::forward(_block)); + } + template inline void emplace(TArgs ... _args) { //m_stack.emplace(_args); m_stack.emplace_back(_args...); @@ -173,7 +178,7 @@ class ProfileManager final typedef profiler::guard_lock guard_lock_t; typedef std::map map_of_threads_stacks; - typedef std::vector block_descriptors_t; + typedef std::vector block_descriptors_t; map_of_threads_stacks m_threads; block_descriptors_t m_descriptors; @@ -190,14 +195,15 @@ public: ~ProfileManager(); template - uint32_t addBlockDescriptor(TArgs ... _args) + const profiler::BaseBlockDescriptor& addBlockDescriptor(TArgs ... _args) { guard_lock_t lock(m_storedSpin); - const auto id = static_cast(m_descriptors.size()); - m_descriptors.emplace_back(m_usedMemorySize, _args...); - return id; + const auto id = static_cast(m_descriptors.size()); + m_descriptors.emplace_back(new profiler::BlockDescriptor(m_usedMemorySize, id, _args...)); + return *m_descriptors.back(); } + void storeBlock(const profiler::BaseBlockDescriptor& _desc, const char* _runtimeName); void beginBlock(profiler::Block& _block); void endBlock(); void setEnabled(bool isEnable); diff --git a/src/reader.cpp b/src/reader.cpp index 7d3348b..da5abc7 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -345,12 +345,63 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic& progr ::profiler::thread_id_t thread_id = 0; inFile.read((char*)&thread_id, sizeof(decltype(thread_id))); + auto& root = threaded_trees[thread_id]; + uint32_t blocks_number_in_thread = 0; inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(blocks_number_in_thread))); - - auto& root = threaded_trees[thread_id]; auto threshold = read_number + blocks_number_in_thread; while (!inFile.eof() && read_number < threshold) + { + EASY_BLOCK("Read context switch", ::profiler::colors::Green); + + ++read_number; + + uint16_t sz = 0; + inFile.read((char*)&sz, sizeof(sz)); + if (sz == 0) + return 0; + + char* data = serialized_blocks[i]; + inFile.read(data, sz); + i += sz; + auto baseData = reinterpret_cast<::profiler::SerializedBlock*>(data); + + if (cpu_frequency != 0) + { + auto t_begin = reinterpret_cast<::profiler::timestamp_t*>(data); + auto t_end = t_begin + 1; + + *t_begin *= 1000000000LL; + *t_begin /= cpu_frequency; + + *t_end *= 1000000000LL; + *t_end /= cpu_frequency; + } + + blocks.emplace_back(); + ::profiler::BlocksTree& tree = blocks.back(); + tree.node = baseData; + const auto block_index = blocks_counter++; + + auto descriptor = descriptors[baseData->id()]; + if (descriptor->type() != ::profiler::BLOCK_TYPE_CONTEXT_SWITCH) + continue; + + root.sync.emplace_back(block_index); + + if (progress.load() < 0) + break; + + progress.store(10 + static_cast(80 * i / memory_size)); + } + + if (progress.load() < 0 || inFile.eof()) + break; + + blocks_number_in_thread = 0; + inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(blocks_number_in_thread))); + threshold = read_number + blocks_number_in_thread; + while (!inFile.eof() && read_number < threshold) { EASY_BLOCK("Read block", ::profiler::colors::Green); @@ -481,60 +532,6 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic& progr break; progress.store(10 + static_cast(80 * i / memory_size)); } - - if (progress.load() < 0 || inFile.eof()) - break; - -#ifdef EASY_STORE_CSWITCH_SEPARATELY - blocks_number_in_thread = 0; - inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(blocks_number_in_thread))); - - threshold = read_number + blocks_number_in_thread; - while (!inFile.eof() && read_number < threshold) - { - EASY_BLOCK("Read context switch", ::profiler::colors::Green); - - ++read_number; - - uint16_t sz = 0; - inFile.read((char*)&sz, sizeof(sz)); - if (sz == 0) - return 0; - - char* data = serialized_blocks[i]; - inFile.read(data, sz); - i += sz; - auto baseData = reinterpret_cast<::profiler::SerializedBlock*>(data); - - if (cpu_frequency != 0) - { - auto t_begin = reinterpret_cast<::profiler::timestamp_t*>(data); - auto t_end = t_begin + 1; - - *t_begin *= 1000000000LL; - *t_begin /= cpu_frequency; - - *t_end *= 1000000000LL; - *t_end /= cpu_frequency; - } - - blocks.emplace_back(); - ::profiler::BlocksTree& tree = blocks.back(); - tree.node = baseData; - const auto block_index = blocks_counter++; - - auto descriptor = descriptors[baseData->id()]; - if (descriptor->type() != ::profiler::BLOCK_TYPE_CONTEXT_SWITCH) - continue; - - root.sync.emplace_back(block_index); - - if (progress.load() < 0) - break; - - progress.store(10 + static_cast(80 * i / memory_size)); - } -#endif } if (progress.load() < 0) @@ -563,12 +560,10 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic& progr statistics_threads.emplace_back(::std::thread([&per_parent_statistics, &per_frame_statistics, &blocks](::profiler::BlocksTreeRoot& root) { -#ifdef EASY_STORE_CSWITCH_SEPARATELY - ::std::sort(root.sync.begin(), root.sync.end(), [&blocks](::profiler::block_index_t left, ::profiler::block_index_t right) - { - return blocks[left].node->begin() < blocks[right].node->begin(); - }); -#endif + //::std::sort(root.sync.begin(), root.sync.end(), [&blocks](::profiler::block_index_t left, ::profiler::block_index_t right) + //{ + // return blocks[left].node->begin() < blocks[right].node->begin(); + //}); for (auto i : root.children) { @@ -601,12 +596,10 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic& progr auto& root = it.second; root.thread_id = it.first; -#ifdef EASY_STORE_CSWITCH_SEPARATELY - ::std::sort(root.sync.begin(), root.sync.end(), [&blocks](::profiler::block_index_t left, ::profiler::block_index_t right) - { - return blocks[left].node->begin() < blocks[right].node->begin(); - }); -#endif + //::std::sort(root.sync.begin(), root.sync.end(), [&blocks](::profiler::block_index_t left, ::profiler::block_index_t right) + //{ + // return blocks[left].node->begin() < blocks[right].node->begin(); + //}); //root.tree.shrink_to_fit(); for (auto i : root.children) From c67bf1dda713c49ddd37c62f5583655c398ab913 Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Sun, 11 Sep 2016 16:53:34 +0300 Subject: [PATCH 05/25] (EasyGraphicsView) Fixed wrong font width calculation issue; Fixed wrong resizeEvent behavior; --- profiler_gui/blocks_graphics_view.cpp | 96 ++++++++++++++++++++------- profiler_gui/blocks_graphics_view.h | 2 + 2 files changed, 74 insertions(+), 24 deletions(-) diff --git a/profiler_gui/blocks_graphics_view.cpp b/profiler_gui/blocks_graphics_view.cpp index b33a9f5..73cefbd 100644 --- a/profiler_gui/blocks_graphics_view.cpp +++ b/profiler_gui/blocks_graphics_view.cpp @@ -95,10 +95,34 @@ inline QRgb selectedItemBorderColor(::profiler::color_t _color) const int FLICKER_INTERVAL = 16; // 60Hz -const auto BG_FONT = QFont("CourierNew", 10, QFont::Bold); -const auto CHRONOMETER_FONT = QFont("CourierNew", 16, QFont::Bold); -const auto ITEMS_FONT = QFont("CourierNew", 10, QFont::Medium); -const auto SELECTED_ITEM_FONT = QFont("CourierNew", 10, QFont::Bold); +QFont EFont(const char* _family, int _size, int _weight = -1) +{ + QFont f; + f.setStyleHint(QFont::Helvetica, QFont::PreferMatch); + f.setFamily(_family); + f.setPointSize(_size); + f.setWeight(_weight); + return f; +} + +const auto BG_FONT = EFont("Helvetica", 10, QFont::Bold); +const auto CHRONOMETER_FONT = EFont("Helvetica", 16, QFont::Bold); +const auto ITEMS_FONT = EFont("Helvetica", 10, QFont::Medium); +const auto SELECTED_ITEM_FONT = EFont("Helvetica", 10, QFont::Bold); + +#ifdef _WIN32 +const qreal FONT_METRICS_FACTOR = 1.05; +#else +const qreal FONT_METRICS_FACTOR = 1.; +#endif + +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif ////////////////////////////////////////////////////////////////////////// @@ -840,7 +864,7 @@ QRectF EasyChronometerItem::boundingRect() const void EasyChronometerItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*) { - const auto sceneView = view(); + auto const sceneView = view(); const auto currentScale = sceneView->scale(); const auto offset = sceneView->offset(); const auto visibleSceneRect = sceneView->visibleSceneRect(); @@ -893,7 +917,7 @@ void EasyChronometerItem::paint(QPainter* _painter, const QStyleOptionGraphicsIt selectedInterval = units2microseconds(selectedInterval); const QString text = ::profiler_gui::timeStringReal(selectedInterval); // Displayed text - const auto textRect = QFontMetricsF(CHRONOMETER_FONT).boundingRect(text); // Calculate displayed text boundingRect + const auto textRect = QFontMetricsF(CHRONOMETER_FONT, sceneView).boundingRect(text); // Calculate displayed text boundingRect const auto rgb = m_color.rgb() & 0x00ffffff; @@ -955,7 +979,8 @@ void EasyChronometerItem::paint(QPainter* _painter, const QStyleOptionGraphicsIt break; } - if (textRect.width() < rect.width()) + const auto textRect_width = textRect.width() * FONT_METRICS_FACTOR; + if (textRect_width < rect.width()) { // Text will be drawed inside rectangle _painter->drawText(rect, textFlags, text); @@ -963,7 +988,7 @@ void EasyChronometerItem::paint(QPainter* _painter, const QStyleOptionGraphicsIt return; } - const auto w = textRect.width() / currentScale; + const auto w = textRect_width / currentScale; if (m_right + w < sceneRight) { // Text will be drawed to the right of rectangle @@ -1039,11 +1064,16 @@ const EasyGraphicsView* EasyChronometerItem::view() const return static_cast(scene()->parent()); } +EasyGraphicsView* EasyChronometerItem::view() +{ + return static_cast(scene()->parent()); +} + ////////////////////////////////////////////////////////////////////////// void EasyBackgroundItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*) { - const auto sceneView = static_cast(scene()->parent()); + auto const sceneView = static_cast(scene()->parent()); const auto visibleSceneRect = sceneView->visibleSceneRect(); const auto currentScale = sceneView->scale(); const auto offset = sceneView->offset(); @@ -1109,7 +1139,7 @@ void EasyBackgroundItem::paint(QPainter* _painter, const QStyleOptionGraphicsIte QLineF marks[20]; qreal first_x = first * sceneStep; - const auto textWidth = QFontMetricsF(_painter->font()).boundingRect(QString::number(static_cast(0.5 + first_x * factor))).width() + 10; + const auto textWidth = QFontMetricsF(_painter->font(), sceneView).width(QString::number(static_cast(0.5 + first_x * factor))) * FONT_METRICS_FACTOR + 10; const int n = 1 + static_cast(textWidth / step); int next = first % n; if (next) @@ -1188,7 +1218,7 @@ void EasyTimelineIndicatorItem::paint(QPainter* _painter, const QStyleOptionGrap ////////////////////////////////////////////////////////////////////////// EasyGraphicsView::EasyGraphicsView(QWidget* _parent) - : QGraphicsView(_parent) + : Parent(_parent) , m_beginTime(::std::numeric_limits::max()) , m_scale(1) , m_offset(0) @@ -1988,8 +2018,24 @@ void EasyGraphicsView::keyReleaseEvent(QKeyEvent* _event) void EasyGraphicsView::resizeEvent(QResizeEvent* _event) { - QGraphicsView::resizeEvent(_event); - updateVisibleSceneRect(); // Update scene visible rect only once + Parent::resizeEvent(_event); + + const QRectF previousRect = m_visibleSceneRect; + updateVisibleSceneRect(); // Update scene visible rect only once + + // Update slider width for scrollbar + const auto windowWidth = m_visibleSceneRect.width() / m_scale; + m_pScrollbar->setSliderWidth(windowWidth); + + // Calculate new offset to save old screen center + const auto deltaWidth = m_visibleSceneRect.width() - previousRect.width(); + m_offset = clamp(0., m_offset - deltaWidth * 0.5 / m_scale, scene()->width() - windowWidth); + + // Update slider position + m_bUpdatingRect = true; // To be sure that updateVisibleSceneRect will not be called by scrollbar change + m_pScrollbar->setValue(m_offset); + m_bUpdatingRect = false; + repaintScene(); // repaint scene } @@ -2198,7 +2244,8 @@ QRectF EasyThreadNameItem::boundingRect() const void EasyThreadNameItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*) { - const auto view = static_cast(scene()->parent())->view(); + auto const parentView = static_cast(scene()->parent()); + const auto view = parentView->view(); const auto& items = view->getItems(); if (items.empty()) return; @@ -2261,7 +2308,7 @@ void EasyThreadNameItem::paint(QPainter* _painter, const QStyleOptionGraphicsIte // Draw information _painter->setFont(CHRONOMETER_FONT); - QFontMetricsF fm(CHRONOMETER_FONT); + QFontMetricsF fm(CHRONOMETER_FONT, parentView); const qreal time1 = view->chronoTime(); const qreal time2 = view->chronoTimeAux(); @@ -2272,13 +2319,13 @@ void EasyThreadNameItem::paint(QPainter* _painter, const QStyleOptionGraphicsIte if (time > 0) { const QString text = ::profiler_gui::timeStringReal(time); // Displayed text - const auto textRect = fm.boundingRect(text); // Calculate displayed text boundingRect - rect.setRect(0, y, w, textRect.height()); + const auto th = fm.height(); // Calculate displayed text height + rect.setRect(0, y, w, th); _painter->setPen(color); _painter->drawText(rect, Qt::AlignCenter, text); - y += textRect.height(); + y += th; } }; @@ -2293,10 +2340,13 @@ void EasyThreadNameItem::setBoundingRect(const QRectF& _rect) ////////////////////////////////////////////////////////////////////////// -EasyThreadNamesWidget::EasyThreadNamesWidget(EasyGraphicsView* _view, int _additionalHeight, QWidget* _parent) : Parent(_parent) +EasyThreadNamesWidget::EasyThreadNamesWidget(EasyGraphicsView* _view, int _additionalHeight, QWidget* _parent) + : Parent(_parent) , m_view(_view) , m_additionalHeight(_additionalHeight + 1) { + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored); + setScene(new QGraphicsScene(this)); setCacheMode(QGraphicsView::CacheNone); @@ -2324,14 +2374,14 @@ void EasyThreadNamesWidget::setVerticalScrollbarRange(int _minValue, int _maxVal void EasyThreadNamesWidget::onTreeChange() { - QSignalBlocker b(this); + const QSignalBlocker b(this); scene()->clear(); - QFontMetricsF fm(BG_FONT); + QFontMetricsF fm(BG_FONT, this); qreal maxLength = 0; const auto& graphicsItems = m_view->getItems(); for (auto graphicsItem : graphicsItems) - maxLength = ::std::max(maxLength, fm.boundingRect(graphicsItem->threadName()).width()); + maxLength = ::std::max(maxLength, fm.width(graphicsItem->threadName()) * FONT_METRICS_FACTOR); auto vbar = verticalScrollBar(); auto viewBar = m_view->verticalScrollBar(); @@ -2348,9 +2398,7 @@ void EasyThreadNamesWidget::onTreeChange() item->setBoundingRect(sceneRect()); scene()->addItem(item); - b.unblock(); setFixedWidth(maxLength); - scene()->update(); } void EasyThreadNamesWidget::onSelectedThreadChange(::profiler::thread_id_t) diff --git a/profiler_gui/blocks_graphics_view.h b/profiler_gui/blocks_graphics_view.h index 3ff4a51..bbf43b7 100644 --- a/profiler_gui/blocks_graphics_view.h +++ b/profiler_gui/blocks_graphics_view.h @@ -247,6 +247,7 @@ private: ///< Returns pointer to the EasyGraphicsView widget. const EasyGraphicsView* view() const; + EasyGraphicsView* view(); }; // END of class EasyChronometerItem. @@ -277,6 +278,7 @@ class EasyGraphicsView : public QGraphicsView private: + typedef QGraphicsView Parent; typedef EasyGraphicsView This; typedef ::std::vector Items; typedef ::std::unordered_set::hasher_t> Keys; From d5d75d6340db3f3a161e8f0e818cf1ab49039734 Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Sun, 11 Sep 2016 16:57:35 +0300 Subject: [PATCH 06/25] (profiler_core) Further work with real-time enable/disable of blocks; (profiler_core) Optimized storeBlock() algorithm, removed unnecessary std::vector; (profiler_core) Writing to std::stringstream and then to file (prepare for streaming over WAN/LAN); (profiler_core) Moved hached_cstr into separate header. --- include/profiler/profiler.h | 257 ++++++++++++++++++-------------- include/profiler/profiler_aux.h | 176 ++++++++++++++++++++++ sample/main.cpp | 2 +- src/block.cpp | 22 ++- src/event_trace_win.cpp | 5 +- src/hashed_cstr.h | 229 ++++++++++++++++++++++++++++ src/outstream.h | 75 ++++++++++ src/profile_manager.cpp | 231 +++++++++++++++++----------- src/profile_manager.h | 207 +++++++++++++++++++------ src/reader.cpp | 107 +------------ 10 files changed, 950 insertions(+), 361 deletions(-) create mode 100644 include/profiler/profiler_aux.h create mode 100644 src/hashed_cstr.h create mode 100644 src/outstream.h diff --git a/include/profiler/profiler.h b/include/profiler/profiler.h index 49491bf..905f025 100644 --- a/include/profiler/profiler.h +++ b/include/profiler/profiler.h @@ -19,67 +19,36 @@ along with this program.If not, see . #ifndef EASY_PROFILER____H_______ #define EASY_PROFILER____H_______ -#ifdef _WIN32 -# define __func__ __FUNCTION__ -# if defined(_MSC_VER) && _MSC_VER <= 1800 -// There is no support for C++11 thread_local keyword prior to Visual Studio 2015. Use __declspec(thread) instead. -# define EASY_THREAD_LOCAL __declspec(thread) -# endif -#elif defined(__GNUC__) -#ifndef __clang__ -# if (__GNUC__ == 4 && __GNUC_MINOR__ < 8) || (__GNUC__ < 4) -// There is no support for C++11 thread_local keyword prior to gcc 4.8. Use __thread instead. -# define EASY_THREAD_LOCAL __thread -#endif -# endif -#if defined ( __clang__ ) -# if (__clang_major__ == 3 && __clang_minor__ < 3) || (__clang_major__ < 3) -# define EASY_THREAD_LOCAL __thread -#endif -#endif - -#endif - -// TODO: Check thread local support for clanv earlier than 3.3 - -#ifndef EASY_THREAD_LOCAL -# define EASY_THREAD_LOCAL thread_local -# define EASY_THREAD_LOCAL_CPP11 -#endif +#include "profiler/profiler_aux.h" #if defined ( __clang__ ) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" #endif #ifndef FULL_DISABLE_PROFILER -#include - -#define EASY_TOKEN_JOIN(x, y) x ## y -#define EASY_TOKEN_CONCATENATE(x, y) EASY_TOKEN_JOIN(x, y) -#define EASY_UNIQUE_BLOCK(x) EASY_TOKEN_CONCATENATE(unique_profiler_mark_name_, x) -#define EASY_UNIQUE_DESC(x) EASY_TOKEN_CONCATENATE(unique_profiler_descriptor_, x) - /** \defgroup profiler Profiler */ -namespace profiler { - template struct NameSwitch final { - static const char* runtime_name(const char* name) { return name; } - static const char* compiletime_name(const char*) { return ""; } - }; +/** If != 0 then EasyProfiler will measure time for blocks storage expansion. +If 0 then EasyProfiler will be compiled without blocks of code responsible +for measuring these events. - template <> struct NameSwitch final { - static const char* runtime_name(const char*) { return ""; } - static const char* compiletime_name(const char* name) { return name; } - }; -} // END of namespace profiler. +These are "EasyProfiler.ExpandStorage" blocks on a diagram. -#define EASY_COMPILETIME_NAME(name) ::profiler::NameSwitch<::std::is_reference::value>::compiletime_name(name) -#define EASY_RUNTIME_NAME(name) ::profiler::NameSwitch<::std::is_reference::value>::runtime_name(name) +\ingroup profiler +*/ +# define EASY_MEASURE_STORAGE_EXPAND 0 +/** If true then "EasyProfiler.ExpandStorage" events are enabled by default and will be +writed to output file or translated over the net. +If false then you need to enable these events via GUI if you'll want to see them. + +\ingroup profiler +*/ +# define EASY_STORAGE_EXPAND_ENABLED true /** Macro of beginning of block with custom name and color. @@ -88,6 +57,8 @@ namespace profiler { void foo() { // some code ... + + EASY_BLOCK("Check something", profiler::DISABLED); // Disabled block (There is possibility to enable this block later via GUI) if(something){ EASY_BLOCK("Calling bar()"); // Block with default color bar(); @@ -96,6 +67,10 @@ namespace profiler { EASY_BLOCK("Calling baz()", profiler::colors::Red); // Red block baz(); } + 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 + // some another code... } \endcode @@ -103,9 +78,9 @@ Block will be automatically completed by destructor. \ingroup profiler */ -#define EASY_BLOCK(name, ...)\ - static const ::profiler::BaseBlockDescriptor& EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(EASY_COMPILETIME_NAME(name), __FILE__, __LINE__,\ - ::profiler::BLOCK_TYPE_BLOCK , ## __VA_ARGS__);\ +# define EASY_BLOCK(name, ...)\ + static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\ + EASY_COMPILETIME_NAME(name), __FILE__, __LINE__, ::profiler::BLOCK_TYPE_BLOCK, ::profiler::extract_color(__VA_ARGS__));\ ::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name));\ ::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable @@ -122,15 +97,20 @@ Block will be automatically completed by destructor. EASY_FUNCTION(profiler::colors::Green); // Green block with name="bar" //some code... } + + void baz(){ + EASY_FUNCTION(profiler::DISABLED); // Disabled block with name="baz" and default color (There is possibility to enable this block later via GUI) + // som code... + } \endcode Name of the block automatically created with function name. \ingroup profiler */ -#define EASY_FUNCTION(...)\ - static const ::profiler::BaseBlockDescriptor& EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(__func__, __FILE__, __LINE__,\ - ::profiler::BLOCK_TYPE_BLOCK , ## __VA_ARGS__);\ +# define EASY_FUNCTION(...)\ + static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\ + __func__, __FILE__, __LINE__, ::profiler::BLOCK_TYPE_BLOCK, ::profiler::extract_color(__VA_ARGS__));\ ::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(EASY_UNIQUE_DESC(__LINE__), "");\ ::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable @@ -157,7 +137,7 @@ int foo() \ingroup profiler */ -#define EASY_END_BLOCK ::profiler::endBlock(); +# define EASY_END_BLOCK ::profiler::endBlock(); /** Macro of creating event with custom name and color. @@ -168,20 +148,23 @@ will end previously opened EASY_BLOCK or EASY_FUNCTION. \ingroup profiler */ -#define EASY_EVENT(name, ...)\ - static const ::profiler::BaseBlockDescriptor& EASY_UNIQUE_DESC(__LINE__) = \ - ::profiler::registerDescription(EASY_COMPILETIME_NAME(name), __FILE__, __LINE__, ::profiler::BLOCK_TYPE_EVENT , ## __VA_ARGS__);\ +# define EASY_EVENT(name, ...)\ + static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__) = \ + ::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__), EASY_COMPILETIME_NAME(name), __FILE__, __LINE__,\ + ::profiler::BLOCK_TYPE_EVENT, ::profiler::extract_color(__VA_ARGS__));\ ::profiler::storeBlock(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name)); /** Macro enabling profiler + \ingroup profiler */ -#define EASY_PROFILER_ENABLE ::profiler::setEnabled(true); +# define EASY_PROFILER_ENABLE ::profiler::setEnabled(true); /** Macro disabling profiler + \ingroup profiler */ -#define EASY_PROFILER_DISABLE ::profiler::setEnabled(false); +# define EASY_PROFILER_DISABLE ::profiler::setEnabled(false); /** Macro of naming current thread. @@ -189,7 +172,7 @@ If this thread has been already named then nothing changes. \ingroup profiler */ -#define EASY_THREAD(name)\ +# define EASY_THREAD(name)\ EASY_THREAD_LOCAL static const char* EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = nullptr;\ if (EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) == nullptr)\ EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = ::profiler::setThreadName(name, __FILE__, __func__, __LINE__); @@ -200,39 +183,31 @@ This is only for user comfort. There is no difference for EasyProfiler GUI betwe \ingroup profiler */ -#define EASY_MAIN_THREAD EASY_THREAD("Main") +# define EASY_MAIN_THREAD EASY_THREAD("Main") #else -#define EASY_BLOCK(...) -#define EASY_FUNCTION(...) -#define EASY_END_BLOCK -#define EASY_PROFILER_ENABLE -#define EASY_PROFILER_DISABLE -#define EASY_EVENT(...) -#define EASY_THREAD(...) -#define EASY_MAIN_THREAD +# define EASY_MEASURE_STORAGE_EXPAND 0 +# define EASY_STORAGE_EXPAND_ENABLED false +# define EASY_BLOCK(...) +# define EASY_FUNCTION(...) +# define EASY_END_BLOCK +# define EASY_PROFILER_ENABLE +# define EASY_PROFILER_DISABLE +# define EASY_EVENT(...) +# define EASY_THREAD(...) +# define EASY_MAIN_THREAD #endif -#include -#include -#include "profiler/profiler_colors.h" - -#ifdef _WIN32 -#ifdef _BUILD_PROFILER -#define PROFILER_API __declspec(dllexport) -#else -#define PROFILER_API __declspec(dllimport) -#endif -#else -#define PROFILER_API -#endif +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// class ProfileManager; +class ThreadStorage; namespace profiler { - class Block; - class BaseBlockDescriptor; + ////////////////////////////////////////////////////////////////////// + // Core types typedef uint64_t timestamp_t; typedef uint32_t thread_id_t; @@ -249,17 +224,7 @@ namespace profiler { }; typedef BlockType block_type_t; - extern "C" { - PROFILER_API const BaseBlockDescriptor& registerDescription(const char* _compiletimeName, const char* _filename, int _line, block_type_t _block_type, color_t _color = ::profiler::colors::Default); - PROFILER_API void storeBlock(const BaseBlockDescriptor& _desc, const char* _runtimeName); - PROFILER_API void beginBlock(Block& _block); - PROFILER_API void endBlock(); - PROFILER_API void setEnabled(bool _isEnable); - PROFILER_API uint32_t dumpBlocksToFile(const char* _filename); - PROFILER_API const char* setThreadName(const char* _name, const char* _filename, const char* _funcname, int _line); - PROFILER_API void setContextSwitchLogFilename(const char* _name); - PROFILER_API const char* getContextSwitchLogFilename(); - } + //*********************************************** #pragma pack(push,1) class PROFILER_API BaseBlockDescriptor @@ -274,7 +239,7 @@ namespace profiler { 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 - BaseBlockDescriptor(block_id_t _id, int _line, block_type_t _block_type, color_t _color); + BaseBlockDescriptor(block_id_t _id, bool _enabled, int _line, block_type_t _block_type, color_t _color); public: @@ -283,22 +248,10 @@ namespace profiler { inline color_t color() const { return m_color; } inline block_type_t type() const { return m_type; } inline bool enabled() const { return m_enabled; } - }; - class PROFILER_API BlockDescriptor final : public BaseBlockDescriptor - { - friend ::ProfileManager; + }; // END of class BaseBlockDescriptor. - 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 - - public: - - BlockDescriptor(uint64_t& _used_mem, block_id_t _id, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color); - - inline const char* name() const { return m_name; } - inline const char* file() const { return m_filename; } - }; + //*********************************************** class PROFILER_API BaseBlockData { @@ -325,20 +278,52 @@ namespace profiler { private: BaseBlockData() = delete; - }; + + }; // END of class BaseBlockData. #pragma pack(pop) + //*********************************************** + + class PROFILER_API BlockDescriptor final : public BaseBlockDescriptor + { + 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 + + BlockDescriptor(uint64_t& _used_mem, bool _enabled, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color); + void setId(block_id_t _id); + + public: + + BlockDescriptor(uint64_t& _used_mem, block_id_t _id, bool _enabled, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color); + + inline const char* name() const { + return m_name; + } + + inline const char* file() const { + return m_filename; + } + + }; // END of class BlockDescriptor. + + //*********************************************** + class PROFILER_API Block final : public BaseBlockData { friend ::ProfileManager; + friend ::ThreadStorage; const char* m_name; bool m_enabled; private: + void start(); + void start(timestamp_t _time); void finish(); - void finish(timestamp_t _end_time); + void finish(timestamp_t _time); inline bool finished() const { return m_end >= m_begin; } inline bool enabled() const { return m_enabled; } @@ -346,18 +331,58 @@ namespace profiler { Block(Block&& that); Block(const BaseBlockDescriptor& _desc, const char* _runtimeName); - Block(timestamp_t _begin_time, block_id_t _id, bool _enabled, const char* _runtimeName); + Block(timestamp_t _begin_time, block_id_t _id, const char* _runtimeName); ~Block(); inline const char* name() const { return m_name; } - }; + + }; // END of class Block. + + //*********************************************** + + class PROFILER_API BlockDescRef final + { + const BaseBlockDescriptor& m_desc; + + public: + + BlockDescRef(const BaseBlockDescriptor& _desc) : m_desc(_desc) { } + inline operator const BaseBlockDescriptor& () const { return m_desc; } + ~BlockDescRef(); + + private: + + BlockDescRef() = delete; + BlockDescRef(const BlockDescRef&) = delete; + BlockDescRef& operator = (const BlockDescRef&) = delete; + + }; // END of class BlockDescRef. + + ////////////////////////////////////////////////////////////////////// + // Core API + + extern "C" { + PROFILER_API const BaseBlockDescriptor& registerDescription(bool _enabled, const char* _compiletimeName, const char* _filename, int _line, block_type_t _block_type, color_t _color); + PROFILER_API void storeBlock(const BaseBlockDescriptor& _desc, const char* _runtimeName); + PROFILER_API void beginBlock(Block& _block); + PROFILER_API void endBlock(); + PROFILER_API void setEnabled(bool _isEnable); + PROFILER_API uint32_t dumpBlocksToFile(const char* _filename); + PROFILER_API const char* setThreadName(const char* _name, const char* _filename, const char* _funcname, int _line); + PROFILER_API void setContextSwitchLogFilename(const char* _name); + PROFILER_API const char* getContextSwitchLogFilename(); + } + + inline void setEnabled(::profiler::EasyEnableFlag _isEnable) { + setEnabled(_isEnable == ::profiler::ENABLED); + } ////////////////////////////////////////////////////////////////////// } // END of namespace profiler. #if defined ( __clang__ ) -#pragma clang diagnostic pop +# pragma clang diagnostic pop #endif #endif // EASY_PROFILER____H_______ diff --git a/include/profiler/profiler_aux.h b/include/profiler/profiler_aux.h new file mode 100644 index 0000000..5ab3a4b --- /dev/null +++ b/include/profiler/profiler_aux.h @@ -0,0 +1,176 @@ +/************************************************************************ +* file name : profiler_aux.h +* ----------------- : +* creation time : 2016/06/11 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : This file contains auxiliary profiler macros and funcitons. +* ----------------- : +* change log : * 2016/06/11 Victor Zarubkin: Moved sources from profiler.h +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016 Sergey Yagovtsev, Victor Zarubkin +* : +* : This program is free software : you can redistribute it and / or modify +* : it under the terms of the GNU General Public License as published by +* : the Free Software Foundation, either version 3 of the License, or +* : (at your option) any later version. +* : +* : This program is distributed in the hope that it will be useful, +* : but WITHOUT ANY WARRANTY; without even the implied warranty of +* : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +* : GNU General Public License for more details. +* : +* : You should have received a copy of the GNU General Public License +* : along with this program.If not, see . +************************************************************************/ + +#ifndef EASY_PROFILER__AUX__H_______ +#define EASY_PROFILER__AUX__H_______ + +#include +#include +#include "profiler/profiler_colors.h" + +////////////////////////////////////////////////////////////////////////// + +#ifdef _WIN32 + +# define __func__ __FUNCTION__ +# if defined(_MSC_VER) && _MSC_VER <= 1800 +// There is no support for C++11 thread_local keyword prior to Visual Studio 2015. Use __declspec(thread) instead. +# define EASY_THREAD_LOCAL __declspec(thread) +# endif +# ifdef _BUILD_PROFILER +# define PROFILER_API __declspec(dllexport) +# else +# define PROFILER_API __declspec(dllimport) +# endif + +#elif defined (__clang__) + +# if (__clang_major__ == 3 && __clang_minor__ < 3) || (__clang_major__ < 3) +# define EASY_THREAD_LOCAL __thread +# endif + +#elif defined(__GNUC__) + +# if (__GNUC__ == 4 && __GNUC_MINOR__ < 8) || (__GNUC__ < 4) +// There is no support for C++11 thread_local keyword prior to gcc 4.8. Use __thread instead. +# define EASY_THREAD_LOCAL __thread +# endif + +#endif + +// TODO: Check thread_local support for clang earlier than 3.3 + +#ifndef EASY_THREAD_LOCAL +# define EASY_THREAD_LOCAL thread_local +# define EASY_THREAD_LOCAL_CPP11 +#endif + +#ifndef PROFILER_API +# define PROFILER_API +#endif + +////////////////////////////////////////////////////////////////////////// + +namespace profiler { + + enum EasyEnableFlag : uint8_t + { + DISABLED = 0, + ENABLED = 1 + }; + +} + +////////////////////////////////////////////////////////////////////////// + +#ifndef FULL_DISABLE_PROFILER + +#include + +# define EASY_STRINGIFY(a) #a +# define EASY_TOKEN_JOIN(x, y) x ## y +# define EASY_TOKEN_CONCATENATE(x, y) EASY_TOKEN_JOIN(x, y) +# define EASY_UNIQUE_BLOCK(x) EASY_TOKEN_CONCATENATE(unique_profiler_mark_name_, x) +# define EASY_UNIQUE_DESC(x) EASY_TOKEN_CONCATENATE(unique_profiler_descriptor_, x) + +namespace profiler { + + template struct NameSwitch final { + static const char* runtime_name(const char* name) { return name; } + static const char* compiletime_name(const char*, const char* autoGeneratedName) { return autoGeneratedName; } + }; + + template <> struct NameSwitch final { + static const char* runtime_name(const char*) { return ""; } + static const char* compiletime_name(const char* name, const char*) { return name; } + }; + + //*********************************************** + + inline color_t extract_color() { + return ::profiler::colors::Default; + } + + template + inline color_t extract_color(::profiler::EasyEnableFlag, TArgs...) { + return ::profiler::colors::Default; + } + + template + inline color_t extract_color(color_t _color, TArgs...) { + return _color; + } + + template + inline color_t extract_color(T, color_t _color, TArgs...) { + return _color; + } + + template + inline color_t extract_color(TArgs...) { + static_assert(false, "No profiler::color_t in arguments list for EASY_BLOCK(name, ...)!"); + return ::profiler::colors::Default; + } + + //*********************************************** + + inline bool extract_enable_flag() { + return true; + } + + template + inline bool extract_enable_flag(T, ::profiler::EasyEnableFlag _flag, TArgs...) { + return _flag == ::profiler::ENABLED; + } + + template + inline bool extract_enable_flag(::profiler::EasyEnableFlag _flag, TArgs...) { + return _flag == ::profiler::ENABLED; + } + + template + inline bool extract_enable_flag(TArgs...) { + static_assert(sizeof...(TArgs) < 2, "No EasyEnableFlag in arguments list for EASY_BLOCK(name, ...)!"); + return true; + } + + //*********************************************** + +} // END of namespace profiler. + +# define EASY_UNIQUE_LINE_ID __FILE__ ":" EASY_STRINGIFY(__LINE__) +# define EASY_COMPILETIME_NAME(name) ::profiler::NameSwitch<::std::is_reference::value>::compiletime_name(name, EASY_UNIQUE_LINE_ID) +# define EASY_RUNTIME_NAME(name) ::profiler::NameSwitch<::std::is_reference::value>::runtime_name(name) + +#endif // FULL_DISABLE_PROFILER + +////////////////////////////////////////////////////////////////////////// + +#endif // EASY_PROFILER__AUX__H_______ diff --git a/sample/main.cpp b/sample/main.cpp index 143fb94..c741a24 100644 --- a/sample/main.cpp +++ b/sample/main.cpp @@ -105,7 +105,7 @@ void prepareRender(){ int multPhys(int i) { - EASY_FUNCTION(profiler::colors::Red700); + EASY_FUNCTION(profiler::colors::Red700, profiler::DISABLED); return i * i * i * i / 100; } diff --git a/src/block.cpp b/src/block.cpp index 48b2ce3..9d7597a 100644 --- a/src/block.cpp +++ b/src/block.cpp @@ -68,25 +68,37 @@ Block::Block(Block&& that) } Block::Block(const BaseBlockDescriptor& _descriptor, const char* _runtimeName) - : Block(getCurrentTime(), _descriptor.id(), _descriptor.enabled(), _runtimeName) + : BaseBlockData(_descriptor.enabled() ? getCurrentTime() : 1ULL, _descriptor.id()) + , m_name(_runtimeName) + , m_enabled(_descriptor.enabled()) { } -Block::Block(timestamp_t _begin_time, block_id_t _descriptor_id, bool _enabled, const char* _runtimeName) +Block::Block(timestamp_t _begin_time, block_id_t _descriptor_id, const char* _runtimeName) : BaseBlockData(_begin_time, _descriptor_id) , m_name(_runtimeName) - , m_enabled(_enabled) + , m_enabled(true) { } +void Block::start() +{ + m_begin = getCurrentTime(); +} + +void Block::start(timestamp_t _time) +{ + m_begin = _time; +} + void Block::finish() { m_end = getCurrentTime(); } -void Block::finish(timestamp_t _end_time) +void Block::finish(timestamp_t _time) { - m_end = _end_time; + m_end = _time; } Block::~Block() diff --git a/src/event_trace_win.cpp b/src/event_trace_win.cpp index d60ed94..5550ba1 100644 --- a/src/event_trace_win.cpp +++ b/src/event_trace_win.cpp @@ -43,12 +43,12 @@ namespace profiler { if (sizeof(CSwitch) != _traceEvent->UserDataLength) return; - //EASY_FUNCTION(::profiler::colors::Red); + //EASY_FUNCTION(::profiler::colors::White); auto _contextSwitchEvent = reinterpret_cast(_traceEvent->UserData); const auto time = static_cast<::profiler::timestamp_t>(_traceEvent->EventHeader.TimeStamp.QuadPart); - static const auto& desc = MANAGER.addBlockDescriptor("OS.ContextSwitch", __FILE__, __LINE__, ::profiler::BLOCK_TYPE_CONTEXT_SWITCH, ::profiler::colors::White); + static const auto& desc = MANAGER.addBlockDescriptor(true, "OS.ContextSwitch", __FILE__, __LINE__, ::profiler::BLOCK_TYPE_CONTEXT_SWITCH, ::profiler::colors::White); MANAGER.beginContextSwitch(_contextSwitchEvent->OldThreadId, time, desc.id()); MANAGER.endContextSwitch(_contextSwitchEvent->NewThreadId, time); } @@ -145,7 +145,6 @@ namespace profiler { m_stubThread = ::std::move(::std::thread([this]() { EASY_THREAD("EasyProfiler.ETW"); - //EASY_BLOCK("ProcessTrace()", ::profiler::colors::Red); ProcessTrace(&m_openedHandle, 1, 0, 0); })); diff --git a/src/hashed_cstr.h b/src/hashed_cstr.h new file mode 100644 index 0000000..0691c34 --- /dev/null +++ b/src/hashed_cstr.h @@ -0,0 +1,229 @@ +/************************************************************************ +* file name : hashed_str.h +* ----------------- : +* creation time : 2016/09/11 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains definition of C-strings with calculated hash-code. +* : These strings may be used as optimized keys for std::unordered_map. +* ----------------- : +* change log : * 2016/09/11 Victor Zarubkin: Initial commit. Moved sources from reader.cpp +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016 Sergey Yagovtsev, Victor Zarubkin +* : +* : This program is free software : you can redistribute it and / or modify +* : it under the terms of the GNU General Public License as published by +* : the Free Software Foundation, either version 3 of the License, or +* : (at your option) any later version. +* : +* : This program is distributed in the hope that it will be useful, +* : but WITHOUT ANY WARRANTY; without even the implied warranty of +* : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +* : GNU General Public License for more details. +* : +* : You should have received a copy of the GNU General Public License +* : along with this program.If not, see . +************************************************************************/ + +#ifndef EASY_PROFILER__HASHED_CSTR__H_ +#define EASY_PROFILER__HASHED_CSTR__H_ + +#include +#include +#include + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +#ifdef _WIN32 + +namespace profiler { + + /** \brief Simple C-string pointer with length. + + It is used as base class for a key in std::unordered_map. + It is used to get better performance than std::string. + It simply stores a pointer and a length, there is no + any memory allocation and copy. + + \warning Make sure you know what you are doing. You have to be sure that + pointed C-string will exist until you finish using this cstring. + + \ingroup profiler + */ + class cstring + { + protected: + + const char* m_str; + size_t m_len; + + public: + + explicit cstring(const char* _str) : m_str(_str), m_len(strlen(_str)) + { + } + + cstring(const cstring&) = default; + cstring& operator = (const cstring&) = default; + + inline bool operator == (const cstring& _other) const + { + return m_len == _other.m_len && !strncmp(m_str, _other.m_str, m_len); + } + + inline bool operator != (const cstring& _other) const + { + return !operator == (_other); + } + + inline bool operator < (const cstring& _other) const + { + if (m_len == _other.m_len) + { + return strncmp(m_str, _other.m_str, m_len) < 0; + } + + return m_len < _other.m_len; + } + + }; // END of class cstring. + + /** \brief cstring with precalculated hash. + + This is used to calculate hash for C-string and to cache it + to be used in the future without recurring hash calculatoin. + + \note This class is used as a key in std::unordered_map. + + \ingroup profiler + */ + class hashed_cstr final : public cstring + { + typedef cstring Parent; + + size_t m_hash; + + public: + + explicit hashed_cstr(const char* _str) : Parent(_str), m_hash(0) + { + m_hash = ::std::_Hash_seq((const unsigned char *)m_str, m_len); + } + + hashed_cstr(const hashed_cstr&) = default; + hashed_cstr& operator = (const hashed_cstr&) = default; + + inline bool operator == (const hashed_cstr& _other) const + { + return m_hash == _other.m_hash && Parent::operator == (_other); + } + + inline bool operator != (const hashed_cstr& _other) const + { + return !operator == (_other); + } + + inline size_t hcode() const + { + return m_hash; + } + + }; // END of class hashed_cstr. + +} // END of namespace profiler. + +namespace std { + + /** \brief Simply returns precalculated hash of a C-string. */ + template <> struct hash<::profiler::hashed_cstr> { + typedef ::profiler::hashed_cstr argument_type; + typedef size_t result_type; + inline size_t operator () (const ::profiler::hashed_cstr& _str) const { + return _str.hcode(); + } + }; + +} // END of namespace std. + +#else //////////////////////////////////////////////////////////////////// + +// TODO: Create hashed_cstr for Linux (need to use Linux version of std::_Hash_seq) + +#endif + +namespace profiler { + + class hashed_stdstring final + { + ::std::string m_str; + size_t m_hash; + + public: + + hashed_stdstring(const char* _str) : m_str(_str), m_hash(::std::hash<::std::string>()(m_str)) + { + } + + hashed_stdstring(const ::std::string& _str) : m_str(_str), m_hash(::std::hash<::std::string>()(m_str)) + { + } + + hashed_stdstring(::std::string&& _str) : m_str(::std::forward<::std::string&&>(_str)), m_hash(::std::hash<::std::string>()(m_str)) + { + } + + hashed_stdstring(hashed_stdstring&& _other) : m_str(::std::move(_other.m_str)), m_hash(_other.m_hash) + { + } + + hashed_stdstring(const hashed_stdstring&) = default; + hashed_stdstring& operator = (const hashed_stdstring&) = default; + + hashed_stdstring& operator = (hashed_stdstring&& _other) + { + m_str = ::std::move(_other.m_str); + m_hash = _other.m_hash; + return *this; + } + + inline bool operator == (const hashed_stdstring& _other) const + { + return m_hash == _other.m_hash && m_str == _other.m_str; + } + + inline bool operator != (const hashed_stdstring& _other) const + { + return !operator == (_other); + } + + inline size_t hcode() const + { + return m_hash; + } + + }; // END of class hashed_stdstring. + +} // END of namespace profiler. + +namespace std { + + /** \brief Simply returns precalculated hash of a std::string. */ + template <> struct hash<::profiler::hashed_stdstring> { + typedef ::profiler::hashed_stdstring argument_type; + typedef size_t result_type; + inline size_t operator () (const ::profiler::hashed_stdstring& _str) const { + return _str.hcode(); + } + }; + +} // END of namespace std. + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +#endif // EASY_PROFILER__HASHED_CSTR__H_ diff --git a/src/outstream.h b/src/outstream.h new file mode 100644 index 0000000..ee82b1c --- /dev/null +++ b/src/outstream.h @@ -0,0 +1,75 @@ +/************************************************************************ +* file name : outstream.h +* ----------------- : +* creation time : 2016/09/11 +* authors : Sergey Yagovtsev, Victor Zarubkin +* emails : yse.sey@gmail.com, v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains definition of output stream helpers. +* ----------------- : +* change log : * 2016/09/11 Victor Zarubkin: Initial commit. Moved sources from profiler_manager.h/.cpp +* : +* : * +* ----------------- : +* license : Lightweight profiler library for c++ +* : Copyright(C) 2016 Sergey Yagovtsev, Victor Zarubkin +* : +* : This program is free software : you can redistribute it and / or modify +* : it under the terms of the GNU General Public License as published by +* : the Free Software Foundation, either version 3 of the License, or +* : (at your option) any later version. +* : +* : This program is distributed in the hope that it will be useful, +* : but WITHOUT ANY WARRANTY; without even the implied warranty of +* : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +* : GNU General Public License for more details. +* : +* : You should have received a copy of the GNU General Public License +* : along with this program.If not, see . +************************************************************************/ + +#ifndef EASY_PROFILER__OUTPUT_STREAM__H_ +#define EASY_PROFILER__OUTPUT_STREAM__H_ + +#include +#include + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +namespace profiler { + + class OStream final + { + ::std::stringstream m_stream; + + public: + + explicit OStream() : m_stream(std::ios_base::out | std::ios_base::binary) + { + + } + + template void write(const char* _data, T _size) + { + m_stream.write(_data, _size); + } + + template void write(const T& _data) + { + m_stream.write((const char*)&_data, sizeof(T)); + } + + const ::std::stringstream& stream() const + { + return m_stream; + } + + }; // END of class OStream. + +} // END of namespace profiler. + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +#endif // EASY_PROFILER__OUTPUT_STREAM__H_ diff --git a/src/profile_manager.cpp b/src/profile_manager.cpp index 118390c..935e2c7 100644 --- a/src/profile_manager.cpp +++ b/src/profile_manager.cpp @@ -23,14 +23,13 @@ * : You should have received a copy of the GNU General Public License * : along with this program.If not, see . ************************************************************************/ -#include "profile_manager.h" -#include "profiler/serialized_block.h" -#include "event_trace_win.h" +#include #include -#include - #include +#include "profiler/serialized_block.h" +#include "profile_manager.h" +#include "event_trace_win.h" ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// @@ -41,14 +40,16 @@ using namespace profiler; extern decltype(LARGE_INTEGER::QuadPart) CPU_FREQUENCY; #endif +extern timestamp_t getCurrentTime(); + //auto& MANAGER = ProfileManager::instance(); #define MANAGER ProfileManager::instance() extern "C" { - PROFILER_API const BaseBlockDescriptor& registerDescription(const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color) + PROFILER_API const BaseBlockDescriptor& registerDescription(bool _enabled, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color) { - return MANAGER.addBlockDescriptor(_name, _filename, _line, _block_type, _color); + return MANAGER.addBlockDescriptor(_enabled, _name, _filename, _line, _block_type, _color); } PROFILER_API void endBlock() @@ -109,44 +110,87 @@ SerializedBlock::SerializedBlock(const Block& block, uint16_t name_length) ////////////////////////////////////////////////////////////////////////// -BaseBlockDescriptor::BaseBlockDescriptor(block_id_t _id, int _line, block_type_t _block_type, color_t _color) +BaseBlockDescriptor::BaseBlockDescriptor(block_id_t _id, bool _enabled, 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(true) + , m_enabled(_enabled) { } -BlockDescriptor::BlockDescriptor(uint64_t& _used_mem, block_id_t _id, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color) - : BaseBlockDescriptor(_id, _line, _block_type, _color) +BlockDescriptor::BlockDescriptor(uint64_t& _used_mem, 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) , m_name(_name) , m_filename(_filename) { _used_mem += sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2; } +BlockDescriptor::BlockDescriptor(uint64_t& _used_mem, 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) + , m_name(_name) + , m_filename(_filename) +{ + _used_mem += sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2; +} + +void BlockDescriptor::setId(block_id_t _id) +{ + m_id = _id; +} + +BlockDescRef::~BlockDescRef() +{ + MANAGER.markExpired(m_desc.id()); +} + ////////////////////////////////////////////////////////////////////////// void ThreadStorage::storeBlock(const profiler::Block& block) { +#if EASY_MEASURE_STORAGE_EXPAND != 0 + static const auto desc = MANAGER.addBlockDescriptor(EASY_STORAGE_EXPAND_ENABLED, "EasyProfiler.ExpandStorage", __FILE__, __LINE__, profiler::BLOCK_TYPE_BLOCK, profiler::colors::White); +#endif + auto name_length = static_cast(strlen(block.name())); auto size = static_cast(sizeof(BaseBlockData) + name_length + 1); - auto data = blocks.alloc.allocate(size); - ::new (static_cast(data)) SerializedBlock(block, name_length); + +#if EASY_MEASURE_STORAGE_EXPAND != 0 + const bool expanded = desc.enabled() && blocks.closedList.need_expand(size); + profiler::Block b(0ULL, desc.id(), ""); + if (expanded) b.start(); +#endif + + auto data = blocks.closedList.allocate(size); + +#if EASY_MEASURE_STORAGE_EXPAND != 0 + if (expanded) b.finish(); +#endif + + ::new (data) SerializedBlock(block, name_length); blocks.usedMemorySize += size; - blocks.closedList.emplace_back(reinterpret_cast(data)); + +#if EASY_MEASURE_STORAGE_EXPAND != 0 + if (expanded) + { + name_length = 0; + size = static_cast(sizeof(BaseBlockData) + 1); + data = blocks.closedList.allocate(size); + ::new (data) SerializedBlock(b, name_length); + blocks.usedMemorySize += size; + } +#endif } void ThreadStorage::storeCSwitch(const profiler::Block& block) { auto name_length = static_cast(strlen(block.name())); auto size = static_cast(sizeof(BaseBlockData) + name_length + 1); - auto data = sync.alloc.allocate(size); - ::new (static_cast(data)) SerializedBlock(block, name_length); + auto data = sync.closedList.allocate(size); + ::new (data) SerializedBlock(block, name_length); sync.usedMemorySize += size; - sync.closedList.emplace_back(reinterpret_cast(data)); } void ThreadStorage::clearClosed() @@ -159,25 +203,9 @@ void ThreadStorage::clearClosed() EASY_THREAD_LOCAL static ::ThreadStorage* THREAD_STORAGE = nullptr; -// #ifdef _WIN32 -// LPTOP_LEVEL_EXCEPTION_FILTER PREVIOUS_FILTER = NULL; -// LONG WINAPI easyTopLevelExceptionFilter(struct _EXCEPTION_POINTERS* ExceptionInfo) -// { -// std::ofstream testexp("TEST_EXP.txt", std::fstream::binary); -// testexp.write("APPLICATION CRASHED!", strlen("APPLICATION CRASHED!")); -// -// EasyEventTracer::instance().disable(); -// if (PREVIOUS_FILTER) -// return PREVIOUS_FILTER(ExceptionInfo); -// return EXCEPTION_CONTINUE_SEARCH; -// } -// #endif - ProfileManager::ProfileManager() { -// #ifdef _WIN32 -// PREVIOUS_FILTER = SetUnhandledExceptionFilter(easyTopLevelExceptionFilter); -// #endif + m_expiredDescriptors.reserve(1024U); } ProfileManager::~ProfileManager() @@ -197,6 +225,28 @@ ProfileManager& ProfileManager::instance() return m_profileManager; } +void ProfileManager::markExpired(profiler::block_id_t _id) +{ + // Mark block descriptor as expired (descriptor may become expired if it's .dll/.so have been unloaded during application execution). + // We can not delete this descriptor now, because we need to send/write all collected data first. + + guard_lock_t lock(m_storedSpin); + m_expiredDescriptors.push_back(_id); +} + +ThreadStorage& ProfileManager::threadStorage(profiler::thread_id_t _thread_id) +{ + guard_lock_t lock(m_spin); + return m_threads[_thread_id]; +} + +ThreadStorage* ProfileManager::findThreadStorage(profiler::thread_id_t _thread_id) +{ + guard_lock_t lock(m_spin); + auto it = m_threads.find(_thread_id); + return it != m_threads.end() ? &it->second : nullptr; +} + void ProfileManager::storeBlock(const profiler::BaseBlockDescriptor& _desc, const char* _runtimeName) { if (!m_isEnabled || !_desc.enabled()) @@ -226,7 +276,7 @@ void ProfileManager::beginContextSwitch(profiler::thread_id_t _thread_id, profil { auto ts = findThreadStorage(_thread_id); if (ts != nullptr) - ts->sync.openedList.emplace(_time, _id, true, ""); + ts->sync.openedList.emplace(_time, _id, ""); } void ProfileManager::storeContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id) @@ -234,7 +284,7 @@ void ProfileManager::storeContextSwitch(profiler::thread_id_t _thread_id, profil auto ts = findThreadStorage(_thread_id); if (ts != nullptr) { - profiler::Block b(_time, _id, true, ""); + profiler::Block b(_time, _id, ""); b.finish(_time); ts->storeCSwitch(b); } @@ -279,39 +329,16 @@ void ProfileManager::setEnabled(bool isEnable) ////////////////////////////////////////////////////////////////////////// -class FileWriter final -{ - std::ofstream m_file; - -public: - - explicit FileWriter(const char* _filename) : m_file(_filename, std::fstream::binary) { } - - template void write(const char* _data, T _size) { - m_file.write(_data, _size); - } - - template void write(const T& _data) { - m_file.write((const char*)&_data, sizeof(T)); - } - - void writeBlock(const profiler::SerializedBlock* _block) - { - auto sz = static_cast(sizeof(BaseBlockData) + strlen(_block->name()) + 1); - write(sz); - write(_block->data(), sz); - } -}; - -////////////////////////////////////////////////////////////////////////// - -uint32_t ProfileManager::dumpBlocksToFile(const char* filename) +uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream) { const bool wasEnabled = m_isEnabled; if (wasEnabled) ::profiler::setEnabled(false); + #ifndef _WIN32 + // Read thread context switch events from temporary file + uint64_t timestamp; uint32_t thread_from, thread_to; @@ -319,19 +346,16 @@ uint32_t ProfileManager::dumpBlocksToFile(const char* filename) if(infile.is_open()) { - static const auto desc = addBlockDescriptor("OS.ContextSwitch", __FILE__, __LINE__, ::profiler::BLOCK_TYPE_CONTEXT_SWITCH, ::profiler::colors::White); + static const auto& desc = addBlockDescriptor(true, "OS.ContextSwitch", __FILE__, __LINE__, profiler::BLOCK_TYPE_CONTEXT_SWITCH, profiler::colors::White); while (infile >> timestamp >> thread_from >> thread_to) { - beginContextSwitch(thread_from, timestamp, desc); + beginContextSwitch(thread_from, timestamp, desc.id()); endContextSwitch(thread_to, timestamp); } } - #endif - - FileWriter of(filename); - + // 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) @@ -341,53 +365,80 @@ uint32_t ProfileManager::dumpBlocksToFile(const char* filename) blocks_number += static_cast(t.blocks.closedList.size()) + static_cast(t.sync.closedList.size()); } + // Write CPU frequency to let GUI calculate real time value from CPU clocks #ifdef _WIN32 - of.write(CPU_FREQUENCY); + _outputStream.write(CPU_FREQUENCY); #else - of.write(0LL); + _outputStream.write(0LL); #endif - of.write(blocks_number); - of.write(usedMemorySize); - of.write(static_cast(m_descriptors.size())); - of.write(m_usedMemorySize); + // Write blocks number and used memory size + _outputStream.write(blocks_number); + _outputStream.write(usedMemorySize); + _outputStream.write(static_cast(m_descriptors.size())); + _outputStream.write(m_usedMemorySize); + // Write block descriptors for (const auto descriptor : m_descriptors) { const auto name_size = static_cast(strlen(descriptor->name()) + 1); const auto filename_size = static_cast(strlen(descriptor->file()) + 1); const auto size = static_cast(sizeof(profiler::SerializedBlockDescriptor) + name_size + filename_size); - of.write(size); - of.write(*descriptor); - of.write(name_size); - of.write(descriptor->name(), name_size); - of.write(descriptor->file(), filename_size); + _outputStream.write(size); + _outputStream.write(*descriptor); + _outputStream.write(name_size); + _outputStream.write(descriptor->name(), name_size); + _outputStream.write(descriptor->file(), filename_size); } + // Write blocks and context switch events for each thread for (auto& thread_storage : m_threads) { auto& t = thread_storage.second; - of.write(thread_storage.first); + _outputStream.write(thread_storage.first); - of.write(static_cast(t.sync.closedList.size())); - for (auto b : t.sync.closedList) - of.writeBlock(b); + _outputStream.write(t.sync.closedList.size()); + t.sync.closedList.serialize(_outputStream); - of.write(static_cast(t.blocks.closedList.size())); - for (auto b : t.blocks.closedList) - of.writeBlock(b); + _outputStream.write(t.blocks.closedList.size()); + t.blocks.closedList.serialize(_outputStream); t.clearClosed(); } -// if (wasEnabled) -// ::profiler::setEnabled(true); + if (!m_expiredDescriptors.empty()) + { + // Remove all expired block descriptors (descriptor may become expired if it's .dll/.so have been unloaded during application execution) + + std::sort(m_expiredDescriptors.begin(), m_expiredDescriptors.end()); + for (auto it = m_expiredDescriptors.rbegin(), rend = m_expiredDescriptors.rend(); it != rend; ++it) + { + auto id = *it; + delete m_descriptors[id]; + m_descriptors.erase(m_descriptors.begin() + id); + } + m_expiredDescriptors.clear(); + } + + //if (wasEnabled) + // ::profiler::setEnabled(true); return blocks_number; } +uint32_t ProfileManager::dumpBlocksToFile(const char* _filename) +{ + profiler::OStream outputStream; + const auto blocksNumber = dumpBlocksToStream(outputStream); + + std::ofstream of(_filename, std::fstream::binary); + of << outputStream.stream().str(); + + return blocksNumber; +} + const char* ProfileManager::setThreadName(const char* name, const char* filename, const char* _funcname, int line) { if (THREAD_STORAGE == nullptr) @@ -397,7 +448,7 @@ const char* ProfileManager::setThreadName(const char* name, const char* filename if (!THREAD_STORAGE->named) { - const auto& desc = addBlockDescriptor(_funcname, filename, line, profiler::BLOCK_TYPE_THREAD_SIGN, profiler::colors::Black); + const auto& desc = addBlockDescriptor(true, _funcname, filename, line, profiler::BLOCK_TYPE_THREAD_SIGN, profiler::colors::Black); profiler::Block b(desc, name); b.finish(b.begin()); diff --git a/src/profile_manager.h b/src/profile_manager.h index 2b54726..9e40153 100644 --- a/src/profile_manager.h +++ b/src/profile_manager.h @@ -16,14 +16,13 @@ You should have received a copy of the GNU General Public License along with this program.If not, see . **/ -#ifndef ___PROFILER____MANAGER____H______ -#define ___PROFILER____MANAGER____H______ +#ifndef EASY_PROFILER____MANAGER____H______ +#define EASY_PROFILER____MANAGER____H______ #include "profiler/profiler.h" - #include "spin_lock.h" - -#include +#include "outstream.h" +//#include "hashed_cstr.h" #include #include #include @@ -56,46 +55,163 @@ namespace profiler { class SerializedBlock; } ////////////////////////////////////////////////////////////////////////// -template +//#define EASY_ENABLE_ALIGNMENT + +#ifndef EASY_ENABLE_ALIGNMENT +# define EASY_ALIGNED(TYPE, VAR, A) TYPE VAR +# define EASY_MALLOC(MEMSIZE, A) malloc(MEMSIZE) +# define EASY_FREE(MEMPTR) free(MEMPTR) +#else +# if defined(_WIN32) +# define EASY_ALIGNED(TYPE, VAR, A) __declspec(align(A)) TYPE VAR +# define EASY_MALLOC(MEMSIZE, A) _aligned_malloc(MEMSIZE, A) +# define EASY_FREE(MEMPTR) _aligned_free(MEMPTR) +# elif defined(__GNUC__) +# define EASY_ALIGNED(TYPE, VAR, A) TYPE VAR __attribute__(aligned(A)) +# else +# define EASY_ALIGNED(TYPE, VAR, A) TYPE VAR +# endif +#endif + +template class chunk_allocator final { - struct chunk { T data[N]; }; + struct chunk { EASY_ALIGNED(int8_t, data[N], 64); chunk* prev = nullptr; }; - std::list m_chunks; - uint16_t m_size; + struct chunk_list + { + chunk* last = nullptr; + + ~chunk_list() + { + clear(); + } + + void clear() + { + do { + auto p = last; + last = last->prev; + EASY_FREE(p); + } while (last != nullptr); + } + + chunk& back() + { + return *last; + } + + void emplace_back() + { + auto prev = last; + last = ::new (EASY_MALLOC(sizeof(chunk), 64)) chunk(); + last->prev = prev; + *(uint16_t*)last->data = 0; + } + + void invert() + { + chunk* next = nullptr; + + while (last->prev != nullptr) { + auto p = last->prev; + last->prev = next; + next = last; + last = p; + } + + last->prev = next; + } + }; + + //typedef std::list chunk_list; + + chunk_list m_chunks; + uint32_t m_size; + uint16_t m_shift; public: - chunk_allocator() : m_size(0) + chunk_allocator() : m_size(0), m_shift(0) { m_chunks.emplace_back(); } - T* allocate(uint16_t n) + void* allocate(uint16_t n) { - if (m_size + n <= N) + ++m_size; + + if (!need_expand(n)) { - T* data = m_chunks.back().data + m_size; - m_size += n; + int8_t* data = m_chunks.back().data + m_shift; + m_shift += n + sizeof(uint16_t); + + *(uint16_t*)data = n; + data = data + sizeof(uint16_t); + + if (m_shift < N) + *(uint16_t*)(data + n) = 0; + return data; } - m_size = n; + m_shift = n + sizeof(uint16_t); m_chunks.emplace_back(); - return m_chunks.back().data; + auto data = m_chunks.back().data; + + *(uint16_t*)data = n; + data = data + sizeof(uint16_t); + + *(uint16_t*)(data + n) = 0; + return data; + } + + inline bool need_expand(uint16_t n) const + { + return (m_shift + n + sizeof(uint16_t)) > N; + } + + inline uint32_t size() const + { + return m_size; } void clear() { m_size = 0; + m_shift = 0; m_chunks.clear(); m_chunks.emplace_back(); } + + /** Serialize data to stream. + + \warning Data will be cleared after serialization. + */ + void serialize(profiler::OStream& _outputStream) + { + m_chunks.invert(); + + auto current = m_chunks.last; + do { + const int8_t* data = current->data; + uint16_t i = 0; + do { + const uint16_t size = sizeof(uint16_t) + *(uint16_t*)data; + _outputStream.write((const char*)data, size); + data = data + size; + i += size; + } while (i < N && *(uint16_t*)data != 0); + current = current->prev; + } while (current != nullptr); + + clear(); + } }; ////////////////////////////////////////////////////////////////////////// -const uint16_t SIZEOF_CSWITCH = sizeof(profiler::BaseBlockData) + 1; +const uint16_t SIZEOF_CSWITCH = sizeof(profiler::BaseBlockData) + 1 + sizeof(uint16_t); typedef std::vector serialized_list_t; @@ -139,14 +255,12 @@ struct BlocksList final } }; - chunk_allocator alloc; Stack openedList; - serialized_list_t closedList; + chunk_allocator closedList; uint64_t usedMemorySize = 0; void clearClosed() { - serialized_list_t().swap(closedList); - alloc.clear(); + //closedList.clear(); usedMemorySize = 0; } }; @@ -156,8 +270,8 @@ class ThreadStorage final { public: - BlocksList, SIZEOF_CSWITCH * (uint16_t)1024U> blocks; - BlocksList sync; + BlocksList, SIZEOF_CSWITCH * (uint16_t)128U> blocks; + BlocksList sync; std::string name; bool named = false; @@ -172,6 +286,8 @@ public: class ProfileManager final { + friend profiler::BlockDescRef; + ProfileManager(); ProfileManager(const ProfileManager& p) = delete; ProfileManager& operator=(const ProfileManager&) = delete; @@ -179,28 +295,36 @@ class ProfileManager final typedef profiler::guard_lock guard_lock_t; typedef std::map map_of_threads_stacks; typedef std::vector block_descriptors_t; + typedef std::vector expired_ids_t; - map_of_threads_stacks m_threads; - block_descriptors_t m_descriptors; - uint64_t m_usedMemorySize = 0; - profiler::spin_lock m_spin; - profiler::spin_lock m_storedSpin; - bool m_isEnabled = false; + map_of_threads_stacks m_threads; + block_descriptors_t m_descriptors; + expired_ids_t m_expiredDescriptors; + uint64_t m_usedMemorySize = 0; + profiler::spin_lock m_spin; + profiler::spin_lock m_storedSpin; + profiler::block_id_t m_idCounter = 0; + bool m_isEnabled = false; std::string m_csInfoFilename = "/tmp/cs_profiling_info.log"; + uint32_t dumpBlocksToStream(profiler::OStream& _outputStream); + public: static ProfileManager& instance(); ~ProfileManager(); template - const profiler::BaseBlockDescriptor& addBlockDescriptor(TArgs ... _args) + const profiler::BaseBlockDescriptor& addBlockDescriptor(bool _enabledByDefault, TArgs ... _args) { + auto desc = new profiler::BlockDescriptor(m_usedMemorySize, _enabledByDefault, _args...); + guard_lock_t lock(m_storedSpin); - const auto id = static_cast(m_descriptors.size()); - m_descriptors.emplace_back(new profiler::BlockDescriptor(m_usedMemorySize, id, _args...)); - return *m_descriptors.back(); + desc->setId(m_idCounter++); + m_descriptors.emplace_back(desc); + + return *desc; } void storeBlock(const profiler::BaseBlockDescriptor& _desc, const char* _runtimeName); @@ -226,18 +350,9 @@ public: private: - ThreadStorage& threadStorage(profiler::thread_id_t _thread_id) - { - guard_lock_t lock(m_spin); - return m_threads[_thread_id]; - } - - ThreadStorage* findThreadStorage(profiler::thread_id_t _thread_id) - { - guard_lock_t lock(m_spin); - auto it = m_threads.find(_thread_id); - return it != m_threads.end() ? &it->second : nullptr; - } + void markExpired(profiler::block_id_t _id); + ThreadStorage& threadStorage(profiler::thread_id_t _thread_id); + ThreadStorage* findThreadStorage(profiler::thread_id_t _thread_id); }; -#endif +#endif // EASY_PROFILER____MANAGER____H______ diff --git a/src/reader.cpp b/src/reader.cpp index da5abc7..44c7c94 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -43,6 +43,7 @@ ************************************************************************/ #include "profiler/reader.h" +#include "hashed_cstr.h" #include #include #include @@ -89,111 +90,17 @@ namespace profiler { #ifdef _WIN32 -/** \brief Simple C-string pointer with length. - -It is used as base class for a key in std::unordered_map. -It is used to get better performance than std::string. -It simply stores a pointer and a length, there is no -any memory allocation and copy. - -\note It is absolutely safe to store pointer because std::unordered_map, -which uses it as a key, exists only inside fillTreesFromFile function. - -*/ -class cstring -{ -protected: - - const char* str; - size_t str_len; - -public: - - explicit cstring(const char* _str) : str(_str), str_len(strlen(_str)) - { - } - - cstring(const cstring& _other) : str(_other.str), str_len(_other.str_len) - { - } - - inline bool operator == (const cstring& _other) const - { - return str_len == _other.str_len && !strncmp(str, _other.str, str_len); - } - - inline bool operator != (const cstring& _other) const - { - return !operator == (_other); - } - - inline bool operator < (const cstring& _other) const - { - if (str_len == _other.str_len) - { - return strncmp(str, _other.str, str_len) < 0; - } - - return str_len < _other.str_len; - } -}; - -/** \brief cstring with precalculated hash. - -This is used to calculate hash for C-string and to cache it -to be used in the future without recurring hash calculatoin. - -\note This class is used as a key in std::unordered_map. - -*/ -class hashed_cstr : public cstring -{ - typedef cstring Parent; - -public: - - size_t str_hash; - - explicit hashed_cstr(const char* _str) : Parent(_str), str_hash(0) - { - str_hash = ::std::_Hash_seq((const unsigned char *)str, str_len); - } - - hashed_cstr(const hashed_cstr& _other) : Parent(_other), str_hash(_other.str_hash) - { - } - - inline bool operator == (const hashed_cstr& _other) const - { - return str_hash == _other.str_hash && Parent::operator == (_other); - } - - inline bool operator != (const hashed_cstr& _other) const - { - return !operator == (_other); - } -}; - -namespace std { - - /** \brief Simply returns precalculated hash of a C-string. */ - template <> struct hash { - inline size_t operator () (const hashed_cstr& _str) const { - return _str.str_hash; - } - }; - -} - typedef ::std::unordered_map<::profiler::block_id_t, ::profiler::BlockStatistics*, passthrough_hash> StatsMap; -typedef ::std::unordered_map IdMap; + +/** \note It is absolutely safe to use hashed_cstr (which simply stores pointer) because std::unordered_map, +which uses it as a key, exists only inside fillTreesFromFile function. */ +typedef ::std::unordered_map<::profiler::hashed_cstr, ::profiler::block_id_t> IdMap; #else -// TODO: optimize for Linux too -#include +// TODO: Create optimized version of profiler::hashed_cstr for Linux too. typedef ::std::unordered_map<::profiler::block_id_t, ::profiler::BlockStatistics*, passthrough_hash> StatsMap; -typedef ::std::unordered_map<::std::string, ::profiler::block_id_t> IdMap; +typedef ::std::unordered_map<::profiler::hashed_stdstring, ::profiler::block_id_t> IdMap; #endif From 18055c7616721b056d65580661c3fdd972f649f5 Mon Sep 17 00:00:00 2001 From: Sergey Yagovtsev Date: Sun, 11 Sep 2016 16:59:01 +0300 Subject: [PATCH 07/25] test script change --- scripts/test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/test.sh b/scripts/test.sh index ec19240..a7454c7 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -17,7 +17,7 @@ echo "Blocks count, dT prof enabled usec, dT prof disabled usec" > $RESULT_FILE for i in {1..9} do - OBJECTS_COUNT=$(($i*10)) + OBJECTS_COUNT=$(($i*100)) for j in {10..15} do RENDER_COUNT=$(($j*100)) From 542783573dd63177a6882fc543f3ac65526753f9 Mon Sep 17 00:00:00 2001 From: Sergey Yagovtsev Date: Sun, 11 Sep 2016 19:34:01 +0300 Subject: [PATCH 08/25] Linux build --- include/profiler/profiler.h | 14 +++++++------- include/profiler/profiler_aux.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/profiler/profiler.h b/include/profiler/profiler.h index 905f025..b2a1b0a 100644 --- a/include/profiler/profiler.h +++ b/include/profiler/profiler.h @@ -79,8 +79,8 @@ Block will be automatically completed by destructor. \ingroup profiler */ # define EASY_BLOCK(name, ...)\ - static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\ - EASY_COMPILETIME_NAME(name), __FILE__, __LINE__, ::profiler::BLOCK_TYPE_BLOCK, ::profiler::extract_color(__VA_ARGS__));\ + static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__)(::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\ + EASY_COMPILETIME_NAME(name), __FILE__, __LINE__, ::profiler::BLOCK_TYPE_BLOCK, ::profiler::extract_color(__VA_ARGS__)));\ ::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name));\ ::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable @@ -109,8 +109,8 @@ Name of the block automatically created with function name. \ingroup profiler */ # define EASY_FUNCTION(...)\ - static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\ - __func__, __FILE__, __LINE__, ::profiler::BLOCK_TYPE_BLOCK, ::profiler::extract_color(__VA_ARGS__));\ + static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__)(::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\ + __func__, __FILE__, __LINE__, ::profiler::BLOCK_TYPE_BLOCK, ::profiler::extract_color(__VA_ARGS__)));\ ::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(EASY_UNIQUE_DESC(__LINE__), "");\ ::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable @@ -149,9 +149,9 @@ will end previously opened EASY_BLOCK or EASY_FUNCTION. \ingroup profiler */ # define EASY_EVENT(name, ...)\ - static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__) = \ + static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__)(\ ::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__), EASY_COMPILETIME_NAME(name), __FILE__, __LINE__,\ - ::profiler::BLOCK_TYPE_EVENT, ::profiler::extract_color(__VA_ARGS__));\ + ::profiler::BLOCK_TYPE_EVENT, ::profiler::extract_color(__VA_ARGS__)));\ ::profiler::storeBlock(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name)); /** Macro enabling profiler @@ -346,7 +346,7 @@ namespace profiler { public: - BlockDescRef(const BaseBlockDescriptor& _desc) : m_desc(_desc) { } + explicit BlockDescRef(const BaseBlockDescriptor& _desc) : m_desc(_desc) { } inline operator const BaseBlockDescriptor& () const { return m_desc; } ~BlockDescRef(); diff --git a/include/profiler/profiler_aux.h b/include/profiler/profiler_aux.h index 5ab3a4b..53319e1 100644 --- a/include/profiler/profiler_aux.h +++ b/include/profiler/profiler_aux.h @@ -135,7 +135,7 @@ namespace profiler { template inline color_t extract_color(TArgs...) { - static_assert(false, "No profiler::color_t in arguments list for EASY_BLOCK(name, ...)!"); + static_assert(sizeof...(TArgs) < 2, "No profiler::color_t in arguments list for EASY_BLOCK(name, ...)!"); return ::profiler::colors::Default; } From ef310af71dab31b198ae8033ad1c72d409722f42 Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Tue, 13 Sep 2016 22:51:06 +0300 Subject: [PATCH 09/25] Colors tweaking --- include/profiler/profiler_colors.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/profiler/profiler_colors.h b/include/profiler/profiler_colors.h index e441563..3230348 100644 --- a/include/profiler/profiler_colors.h +++ b/include/profiler/profiler_colors.h @@ -329,6 +329,7 @@ namespace profiler { const color_t Red = Red500; const color_t DarkRed = Red900; + const color_t Coral = Red200; const color_t RichRed = 0xffff0000; const color_t Pink = Pink500; const color_t Rose = PinkA100; @@ -355,7 +356,6 @@ namespace profiler { const color_t Lime = Lime500; const color_t Olive = Lime900; const color_t Yellow = Yellow500; - const color_t DarkYellow = DarkRed | DarkGreen; const color_t RichYellow = YellowA200; const color_t Amber = Amber500; const color_t Gold = Amber300; @@ -364,7 +364,6 @@ namespace profiler { const color_t Skin = Orange100; const color_t DeepOrange = DeepOrange500; const color_t Brick = DeepOrange900; - const color_t Coral = DeepOrange300; const color_t Brown = Brown500; const color_t DarkBrown = Brown900; const color_t CreamWhite = Orange50; From f49064116557ef09c6df3c2c961bf678027bec14 Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Tue, 13 Sep 2016 22:53:07 +0300 Subject: [PATCH 10/25] forgotten row --- src/block.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/block.cpp b/src/block.cpp index 9d7597a..4f43f01 100644 --- a/src/block.cpp +++ b/src/block.cpp @@ -63,6 +63,7 @@ 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_end = that.m_end; } From 1b33ad788da4588311c4d764277061e212c02dc1 Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Tue, 13 Sep 2016 23:03:01 +0300 Subject: [PATCH 11/25] (profiler_core) Further work with run-time enable/disable blocks; (profiler_core) Fixed bug with memory corruption in chunk_allocator; (profiler_core) Event tracing now gathering info about process which owns thread; (profiler_core) Thread names are stored before other blocks now; (profiler_core) Removed unused block types: context switch and thread sign; (profiler_core) Added some description for API; (profiler_gui) Context switch events now painted in different colors: red = switch to another process, coral = switch to registered thread in the same process, black = switch to kernel mode (thread id == 0). (profiler_gui) Added popup windows with information about context-switch or block under mouse cursor; --- include/profiler/profiler.h | 155 +++- include/profiler/profiler_aux.h | 9 +- include/profiler/reader.h | 23 +- profiler_gui/blocks_graphics_view.cpp | 813 +++++++++++------- profiler_gui/blocks_graphics_view.h | 9 + profiler_gui/common_types.h | 1 - profiler_gui/graphics_scrollbar.cpp | 4 +- profiler_gui/tree_widget_loader.cpp | 20 +- .../profiler => src}/event_trace_status.h | 0 src/event_trace_win.cpp | 113 ++- src/event_trace_win.h | 16 +- src/hashed_cstr.h | 36 +- src/profile_manager.cpp | 158 ++-- src/profile_manager.h | 81 +- src/reader.cpp | 36 +- 15 files changed, 1008 insertions(+), 466 deletions(-) rename {include/profiler => src}/event_trace_status.h (100%) diff --git a/include/profiler/profiler.h b/include/profiler/profiler.h index b2a1b0a..c43870c 100644 --- a/include/profiler/profiler.h +++ b/include/profiler/profiler.h @@ -29,7 +29,7 @@ along with this program.If not, see . #ifndef FULL_DISABLE_PROFILER /** -\defgroup profiler Profiler +\defgroup profiler EasyProfiler */ /** If != 0 then EasyProfiler will measure time for blocks storage expansion. @@ -50,7 +50,16 @@ If false then you need to enable these events via GUI if you'll want to see them */ # define EASY_STORAGE_EXPAND_ENABLED true -/** Macro of beginning of block with custom name and color. +/** If true then EasyProfiler event tracing is enabled by default +and will be turned on and off when you call profiler::setEnabled(). +Otherwise, it have to be turned on via GUI and then it will be +turned on/off with next calls of profiler::setEnabled(). + +\ingroup profiler +*/ +# define EASY_EVENT_TRACING_ENABLED true + +/** Macro for beginning of a block with custom name and color. \code #include "profiler/profiler.h" @@ -80,11 +89,11 @@ Block will be automatically completed by destructor. */ # define EASY_BLOCK(name, ...)\ static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__)(::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\ - EASY_COMPILETIME_NAME(name), __FILE__, __LINE__, ::profiler::BLOCK_TYPE_BLOCK, ::profiler::extract_color(__VA_ARGS__)));\ + EASY_UNIQUE_LINE_ID, EASY_COMPILETIME_NAME(name), __FILE__, __LINE__, ::profiler::BLOCK_TYPE_BLOCK, ::profiler::extract_color(__VA_ARGS__)));\ ::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name));\ ::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable -/** Macro of beginning of block with function name and custom color. +/** Macro for beginning of a block with function name and custom color. \code #include "profiler/profiler.h" @@ -110,11 +119,11 @@ Name of the block automatically created with function name. */ # define EASY_FUNCTION(...)\ static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__)(::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\ - __func__, __FILE__, __LINE__, ::profiler::BLOCK_TYPE_BLOCK, ::profiler::extract_color(__VA_ARGS__)));\ + EASY_UNIQUE_LINE_ID, __func__, __FILE__, __LINE__, ::profiler::BLOCK_TYPE_BLOCK, ::profiler::extract_color(__VA_ARGS__)));\ ::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(EASY_UNIQUE_DESC(__LINE__), "");\ ::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable -/** Macro of completion of last nearest open block. +/** Macro for completion of last opened block. \code #include "profiler/profiler.h" @@ -139,7 +148,7 @@ int foo() */ # define EASY_END_BLOCK ::profiler::endBlock(); -/** Macro of creating event with custom name and color. +/** Macro for creating event with custom name and color. Event is a block with zero duration and special type. @@ -150,44 +159,61 @@ will end previously opened EASY_BLOCK or EASY_FUNCTION. */ # define EASY_EVENT(name, ...)\ static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__)(\ - ::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__), EASY_COMPILETIME_NAME(name), __FILE__, __LINE__,\ - ::profiler::BLOCK_TYPE_EVENT, ::profiler::extract_color(__VA_ARGS__)));\ - ::profiler::storeBlock(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name)); + ::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__), EASY_UNIQUE_LINE_ID, EASY_COMPILETIME_NAME(name),\ + __FILE__, __LINE__, ::profiler::BLOCK_TYPE_EVENT, ::profiler::extract_color(__VA_ARGS__)));\ + ::profiler::storeEvent(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name)); -/** Macro enabling profiler +/** Macro for enabling profiler. \ingroup profiler */ # define EASY_PROFILER_ENABLE ::profiler::setEnabled(true); -/** Macro disabling profiler +/** Macro for disabling profiler. \ingroup profiler */ # define EASY_PROFILER_DISABLE ::profiler::setEnabled(false); -/** Macro of naming current thread. +/** Macro for current thread registration. -If this thread has been already named then nothing changes. +\note If this thread has been already registered then nothing happens. \ingroup profiler */ # define EASY_THREAD(name)\ EASY_THREAD_LOCAL static const char* EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = nullptr;\ if (EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) == nullptr)\ - EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = ::profiler::setThreadName(name, __FILE__, __func__, __LINE__); + EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = ::profiler::registerThread(name); -/** Macro of naming main thread. +/** Macro for main thread registration. -This is only for user comfort. There is no difference for EasyProfiler GUI between different threads. +This is just for user's comfort. There is no difference for EasyProfiler GUI between different threads. \ingroup profiler */ # define EASY_MAIN_THREAD EASY_THREAD("Main") +# ifndef _WIN32 +/** Macro for setting temporary log-file path for Unix event tracing system. + +\note Default value is "/tmp/cs_profiling_info.log". + +\ingroup profiler +*/ +# define EASY_EVENT_TRACING_SET_LOG(filename) ::profiler::setContextSwitchLogFilename(filename); + +/** Macro returning current path to the temporary log-file for Unix event tracing system. + +\ingroup profiler +*/ +# define EASY_EVENT_TRACING_LOG ::profiler::getContextSwitchLogFilename(); +# endif + #else # define EASY_MEASURE_STORAGE_EXPAND 0 # define EASY_STORAGE_EXPAND_ENABLED false +# define EASY_EVENT_TRACING_ENABLED false # define EASY_BLOCK(...) # define EASY_FUNCTION(...) # define EASY_END_BLOCK @@ -196,6 +222,12 @@ This is only for user comfort. There is no difference for EasyProfiler GUI betwe # define EASY_EVENT(...) # define EASY_THREAD(...) # define EASY_MAIN_THREAD + +# ifndef _WIN32 +# define EASY_EVENT_TRACING_SET_LOG(filename) +# define EASY_EVENT_TRACING_LOG "" +# endif + #endif ////////////////////////////////////////////////////////////////////////// @@ -216,9 +248,7 @@ namespace profiler { enum BlockType : uint8_t { BLOCK_TYPE_EVENT = 0, - BLOCK_TYPE_THREAD_SIGN, BLOCK_TYPE_BLOCK, - BLOCK_TYPE_CONTEXT_SWITCH, BLOCK_TYPES_NUMBER }; @@ -290,9 +320,10 @@ namespace profiler { const char* m_name; ///< Static name of all blocks of the same type (blocks can have dynamic name) which is, in pair with descriptor id, a unique block identifier const char* m_filename; ///< Source file name where this block is declared + bool* m_pEnable; ///< Pointer to the enable flag in unordered_map + bool m_expired; ///< Is this descriptor expired BlockDescriptor(uint64_t& _used_mem, bool _enabled, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color); - void setId(block_id_t _id); public: @@ -336,6 +367,11 @@ namespace profiler { inline const char* name() const { return m_name; } + private: + + Block(const Block&) = delete; + Block& operator = (const Block&) = delete; + }; // END of class Block. //*********************************************** @@ -360,17 +396,80 @@ namespace profiler { ////////////////////////////////////////////////////////////////////// // Core API + // Note: it is better to use macros defined above than a direct calls to API. extern "C" { - PROFILER_API const BaseBlockDescriptor& registerDescription(bool _enabled, const char* _compiletimeName, const char* _filename, int _line, block_type_t _block_type, color_t _color); - PROFILER_API void storeBlock(const BaseBlockDescriptor& _desc, const char* _runtimeName); - PROFILER_API void beginBlock(Block& _block); - PROFILER_API void endBlock(); - PROFILER_API void setEnabled(bool _isEnable); - PROFILER_API uint32_t dumpBlocksToFile(const char* _filename); - PROFILER_API const char* setThreadName(const char* _name, const char* _filename, const char* _funcname, int _line); - PROFILER_API void setContextSwitchLogFilename(const char* _name); + + /** Registers static description of a block. + + It is general information which is common for all such blocks. + Includes color, block type (see BlockType), file-name, line-number, compile-time name of a block and enable-flag. + + \ingroup profiler + */ + PROFILER_API const BaseBlockDescriptor& registerDescription(bool _enabled, const char* _autogenUniqueId, const char* _compiletimeName, const char* _filename, int _line, block_type_t _block_type, color_t _color); + + /** Stores event in the blocks list. + + An event ends instantly and has zero duration. + + \param _desc Reference to the previously registered description. + \param _runtimeName Standard zero-terminated string which will be copied to the events buffer. + + \ingroup profiler + */ + PROFILER_API void storeEvent(const BaseBlockDescriptor& _desc, const char* _runtimeName); + + /** Begins block. + + \ingroup profiler + */ + PROFILER_API void beginBlock(Block& _block); + + /** Ends last started block. + + \ingroup profiler + */ + PROFILER_API void endBlock(); + + /** Enable or disable profiler. + + \ingroup profiler + */ + PROFILER_API void setEnabled(bool _isEnable); + + /** Save all gathered blocks into file. + + \note This also disables profiler. + + \ingroup profiler + */ + PROFILER_API uint32_t dumpBlocksToFile(const char* _filename); + + /** Register current thread and give it a name. + + \note Only first call of registerThread() for the current thread will have an effect. + + \ingroup profiler + */ + PROFILER_API const char* registerThread(const char* _name); + +#ifndef _WIN32 + /** Set temporary log-file path for Unix event tracing system. + + \note Default value is "/tmp/cs_profiling_info.log". + + \ingroup profiler + */ + PROFILER_API void setContextSwitchLogFilename(const char* _name); + + /** Returns current path to the temporary log-file for Unix event tracing system. + + \ingroup profiler + */ PROFILER_API const char* getContextSwitchLogFilename(); +#endif + } inline void setEnabled(::profiler::EasyEnableFlag _isEnable) { diff --git a/include/profiler/profiler_aux.h b/include/profiler/profiler_aux.h index 53319e1..18c2d95 100644 --- a/include/profiler/profiler_aux.h +++ b/include/profiler/profiler_aux.h @@ -86,6 +86,12 @@ namespace profiler { ENABLED = 1 }; + struct passthrough_hash final { + template inline size_t operator () (T _value) const { + return static_cast(_value); + } + }; + } ////////////////////////////////////////////////////////////////////////// @@ -95,6 +101,7 @@ namespace profiler { #include # define EASY_STRINGIFY(a) #a +# define EASY_STRINGIFICATION(a) EASY_STRINGIFY(a) # define EASY_TOKEN_JOIN(x, y) x ## y # define EASY_TOKEN_CONCATENATE(x, y) EASY_TOKEN_JOIN(x, y) # define EASY_UNIQUE_BLOCK(x) EASY_TOKEN_CONCATENATE(unique_profiler_mark_name_, x) @@ -165,7 +172,7 @@ namespace profiler { } // END of namespace profiler. -# define EASY_UNIQUE_LINE_ID __FILE__ ":" EASY_STRINGIFY(__LINE__) +# define EASY_UNIQUE_LINE_ID __FILE__ ":" EASY_STRINGIFICATION(__LINE__) # define EASY_COMPILETIME_NAME(name) ::profiler::NameSwitch<::std::is_reference::value>::compiletime_name(name, EASY_UNIQUE_LINE_ID) # define EASY_RUNTIME_NAME(name) ::profiler::NameSwitch<::std::is_reference::value>::runtime_name(name) diff --git a/include/profiler/reader.h b/include/profiler/reader.h index 0dc38cd..bf35d70 100644 --- a/include/profiler/reader.h +++ b/include/profiler/reader.h @@ -21,8 +21,9 @@ along with this program.If not, see . ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#include +#include #include +#include #include #include "profiler/profiler.h" #include "profiler/serialized_block.h" @@ -175,7 +176,7 @@ namespace profiler { BlocksTree::children_t children; BlocksTree::children_t sync; - const char* thread_name; + std::string thread_name; ::profiler::thread_id_t thread_id; uint16_t depth; @@ -186,7 +187,7 @@ namespace profiler { BlocksTreeRoot(This&& that) : children(::std::move(that.children)) , sync(::std::move(that.sync)) - , thread_name(that.thread_name) + , thread_name(::std::move(that.thread_name)) , thread_id(that.thread_id) , depth(that.depth) { @@ -196,12 +197,24 @@ namespace profiler { { children = ::std::move(that.children); sync = ::std::move(that.sync); - thread_name = that.thread_name; + thread_name = ::std::move(that.thread_name); thread_id = that.thread_id; depth = that.depth; return *this; } + inline bool gotName() const + { + //return thread_name && *thread_name != 0; + return thread_name.front() != 0; + } + + inline const char* name() const + { + //return thread_name; + return thread_name.c_str(); + } + bool operator < (const This& other) const { return thread_id < other.thread_id; @@ -215,7 +228,7 @@ namespace profiler { }; // END of class BlocksTreeRoot. typedef ::profiler::BlocksTree::blocks_t blocks_t; - typedef ::std::map<::profiler::thread_id_t, ::profiler::BlocksTreeRoot> thread_blocks_tree_t; + typedef ::std::unordered_map<::profiler::thread_id_t, ::profiler::BlocksTreeRoot, ::profiler::passthrough_hash> thread_blocks_tree_t; ////////////////////////////////////////////////////////////////////////// diff --git a/profiler_gui/blocks_graphics_view.cpp b/profiler_gui/blocks_graphics_view.cpp index 73cefbd..a54efa9 100644 --- a/profiler_gui/blocks_graphics_view.cpp +++ b/profiler_gui/blocks_graphics_view.cpp @@ -39,6 +39,9 @@ ************************************************************************/ #include +#include +#include +#include #include #include #include @@ -93,7 +96,11 @@ inline QRgb selectedItemBorderColor(::profiler::color_t _color) //const unsigned int TEST_PROGRESSION_BASE = 4; -const int FLICKER_INTERVAL = 16; // 60Hz +const int IDLE_TIMER_INTERVAL = 200; // 5Hz +const uint64_t IDLE_TIME = 400; + +const int FLICKER_INTERVAL = 10; // 100Hz +const qreal FLICKER_FACTOR = 16.0 / FLICKER_INTERVAL; QFont EFont(const char* _family, int _size, int _weight = -1) { @@ -143,7 +150,7 @@ inline T logn(T _value) EasyGraphicsItem::EasyGraphicsItem(uint8_t _index, const::profiler::BlocksTreeRoot& _root) : QGraphicsItem(nullptr) - , m_threadName(*_root.thread_name != 0 ? QString("%1 Thread %2").arg(_root.thread_name).arg(_root.thread_id) : QString("Thread %1").arg(_root.thread_id)) + , m_threadName(_root.gotName() ? QString("%1 Thread %2").arg(_root.name()).arg(_root.thread_id) : QString("Thread %1").arg(_root.thread_id)) , m_pRoot(&_root) , m_index(_index) { @@ -171,13 +178,17 @@ QRectF EasyGraphicsItem::boundingRect() const void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*) { - if (m_levels.empty() || m_levels.front().empty()) + const bool gotItems = !m_levels.empty() && !m_levels.front().empty(); + const bool gotSync = !m_pRoot->sync.empty(); + + if (!gotItems && !gotSync) { return; } const auto sceneView = view(); const auto visibleSceneRect = sceneView->visibleSceneRect(); // Current visible scene rect + const auto visibleBottom = visibleSceneRect.bottom() - 1; const auto currentScale = sceneView->scale(); // Current GraphicsView scale const auto offset = sceneView->offset(); const auto sceneLeft = offset, sceneRight = offset + visibleSceneRect.width() / currentScale; @@ -199,40 +210,27 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* // Search for first visible top-level item - auto& level0 = m_levels.front(); - auto first = ::std::lower_bound(level0.begin(), level0.end(), sceneLeft, [](const ::profiler_gui::EasyBlockItem& _item, qreal _value) + if (gotItems) { - return _item.left() < _value; - }); + auto& level0 = m_levels.front(); + auto first = ::std::lower_bound(level0.begin(), level0.end(), sceneLeft, [](const ::profiler_gui::EasyBlockItem& _item, qreal _value) + { + return _item.left() < _value; + }); - if (first != level0.end()) - { - m_levelsIndexes[0] = first - level0.begin(); - if (m_levelsIndexes[0] > 0) - m_levelsIndexes[0] -= 1; - } - else - { - m_levelsIndexes[0] = static_cast(level0.size() - 1); + if (first != level0.end()) + { + m_levelsIndexes[0] = first - level0.begin(); + if (m_levelsIndexes[0] > 0) + m_levelsIndexes[0] -= 1; + } + else + { + m_levelsIndexes[0] = static_cast(level0.size() - 1); + } } - auto firstSync = ::std::lower_bound(m_pRoot->sync.begin(), m_pRoot->sync.end(), sceneLeft, [&sceneView](::profiler::block_index_t _index, qreal _value) - { - return sceneView->time2position(easyBlock(_index).tree.node->begin()) < _value; - }); - - if (firstSync != m_pRoot->sync.end()) - { - if (firstSync != m_pRoot->sync.begin()) - --firstSync; - } - else if (!m_pRoot->sync.empty()) - { - firstSync = m_pRoot->sync.begin() + m_pRoot->sync.size() - 1; - } - firstSync = m_pRoot->sync.begin(); - // This is to make _painter->drawText() work properly // (it seems there is a bug in Qt5.6 when drawText called for big coordinates, @@ -259,269 +257,275 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* } - static const auto MAX_CHILD_INDEX = ::profiler_gui::numeric_max(); - auto const skip_children = [this, &levelsNumber](short next_level, decltype(::profiler_gui::EasyBlockItem::children_begin) children_begin) - { - // Mark that we would not paint children of current item - if (next_level < levelsNumber && children_begin != MAX_CHILD_INDEX) - m_levels[next_level][children_begin].state = BLOCK_ITEM_DO_NOT_PAINT; - }; - // Iterate through layers and draw visible items - bool selectedItemsWasPainted = false; - const auto visibleBottom = visibleSceneRect.bottom() - 1; - for (uint8_t l = 0; l < levelsNumber; ++l) + if (gotItems) { - auto& level = m_levels[l]; - const short next_level = l + 1; - char state = BLOCK_ITEM_DO_PAINT; - - const auto top = levelY(l); - qreal prevRight = -1e100; - for (unsigned int i = m_levelsIndexes[l], end = static_cast(level.size()); i < end; ++i) + static const auto MAX_CHILD_INDEX = ::profiler_gui::numeric_max(); + auto const skip_children = [this, &levelsNumber](short next_level, decltype(::profiler_gui::EasyBlockItem::children_begin) children_begin) { - auto& item = level[i]; + // Mark that we would not paint children of current item + if (next_level < levelsNumber && children_begin != MAX_CHILD_INDEX) + m_levels[next_level][children_begin].state = BLOCK_ITEM_DO_NOT_PAINT; + }; - if (item.left() > sceneRight) - break; // This is first totally invisible item. No need to check other items. + bool selectedItemsWasPainted = false; + for (uint8_t l = 0; l < levelsNumber; ++l) + { + auto& level = m_levels[l]; + const short next_level = l + 1; + char state = BLOCK_ITEM_DO_PAINT; - if (item.state != BLOCK_ITEM_UNCHANGED) + const auto top = levelY(l); + qreal prevRight = -1e100; + for (unsigned int i = m_levelsIndexes[l], end = static_cast(level.size()); i < end; ++i) { - state = item.state; - } + auto& item = level[i]; - if (item.right() < sceneLeft || state == BLOCK_ITEM_DO_NOT_PAINT || top > visibleBottom || (top + item.totalHeight) < visibleSceneRect.top()) - { - // This item is not visible - skip_children(next_level, item.children_begin); - continue; - } + if (item.left() > sceneRight) + break; // This is first totally invisible item. No need to check other items. - const auto x = item.left() * currentScale - dx; - auto w = item.width() * currentScale; - if (x + w <= prevRight) - { - // This item is not visible - if (w < 20) + if (item.state != BLOCK_ITEM_UNCHANGED) + { + state = item.state; + } + + if (item.right() < sceneLeft || state == BLOCK_ITEM_DO_NOT_PAINT || top > visibleBottom || (top + item.totalHeight) < visibleSceneRect.top()) + { + // This item is not visible skip_children(next_level, item.children_begin); - continue; - } - - const auto& itemBlock = easyBlock(item.block); - int h = 0, flags = 0; - if (w < 20 || !itemBlock.expanded) - { - // Items which width is less than 20 will be painted as big rectangles which are hiding it's children - - //x = item.left() * currentScale - dx; - h = item.totalHeight; - const auto dh = top + h - visibleBottom; - if (dh > 0) - h -= dh; - - if (item.block == EASY_GLOBALS.selected_block) - selectedItemsWasPainted = true; - - const bool colorChange = (previousColor != item.color); - if (colorChange) - { - // Set background color brush for rectangle - previousColor = item.color; - inverseColor = 0xffffffff - previousColor; - textColor = ::profiler_gui::textColorForRgb(previousColor); - brush.setColor(previousColor); - _painter->setBrush(brush); - } - - if (EASY_GLOBALS.draw_graphics_items_borders && (previousPenStyle != Qt::SolidLine || colorChange)) - { - // Restore pen for item which is wide enough to paint borders - previousPenStyle = Qt::SolidLine; - _painter->setPen(BORDERS_COLOR & inverseColor);// BORDERS_COLOR); - } - - if (w < 2) w = 2; - - // Draw rectangle - rect.setRect(x, top, w, h); - _painter->drawRect(rect); - - prevRight = rect.right(); - skip_children(next_level, item.children_begin); - if (w < 20) continue; - - if (item.totalHeight > GRAPHICS_ROW_SIZE) - flags = Qt::AlignCenter; - else if (!(item.width() < 1)) - flags = Qt::AlignHCenter; - } - else - { - if (next_level < levelsNumber && item.children_begin != MAX_CHILD_INDEX) - { - if (m_levelsIndexes[next_level] == MAX_CHILD_INDEX) - { - // Mark first potentially visible child item on next sublevel - m_levelsIndexes[next_level] = item.children_begin; - } - - // Mark children items that we want to draw them - m_levels[next_level][item.children_begin].state = BLOCK_ITEM_DO_PAINT; } - if (item.block == EASY_GLOBALS.selected_block) - selectedItemsWasPainted = true; - - const bool colorChange = (previousColor != item.color); - if (colorChange) + const auto x = item.left() * currentScale - dx; + auto w = item.width() * currentScale; + if (x + w <= prevRight) { - // Set background color brush for rectangle - previousColor = item.color; - inverseColor = 0xffffffff - previousColor; - textColor = ::profiler_gui::textColorForRgb(previousColor); - brush.setColor(previousColor); - _painter->setBrush(brush); + // This item is not visible + if (w < 20) + skip_children(next_level, item.children_begin); + continue; } - if (EASY_GLOBALS.draw_graphics_items_borders && (previousPenStyle != Qt::SolidLine || colorChange)) + const auto& itemBlock = easyBlock(item.block); + const auto& itemDesc = easyDescriptor(itemBlock.tree.node->id()); + + int h = 0, flags = 0; + if (w < 20 || !itemBlock.expanded) { - // Restore pen for item which is wide enough to paint borders - previousPenStyle = Qt::SolidLine; - _painter->setPen(BORDERS_COLOR & inverseColor);// BORDERS_COLOR); - } + // Items which width is less than 20 will be painted as big rectangles which are hiding it's children - // Draw rectangle - //x = item.left() * currentScale - dx; - h = GRAPHICS_ROW_SIZE; - const auto dh = top + h - visibleBottom; - if (dh > 0) - h -= dh; - - rect.setRect(x, top, w, h); - _painter->drawRect(rect); - - if (!(item.width() < 1)) - flags = Qt::AlignHCenter; - - prevRight = rect.right(); - } - - // Draw text----------------------------------- - // calculating text coordinates - auto xtext = x; - if (item.left() < sceneLeft) - { - // if item left border is out of screen then attach text to the left border of the screen - // to ensure text is always visible for items presenting on the screen. - w += (item.left() - sceneLeft) * currentScale; - xtext = sceneLeft * currentScale - dx; - } - - if (item.right() > sceneRight) - { - w -= (item.right() - sceneRight) * currentScale; - } - - rect.setRect(xtext + 1, top, w - 1, h); - - // text will be painted with inverse color - //auto textColor = inverseColor < 0x00808080 ? profiler::colors::Black : profiler::colors::White; - //if (textColor == previousColor) textColor = 0; - _painter->setPen(QColor::fromRgb(textColor)); - - if (item.block == EASY_GLOBALS.selected_block) - _painter->setFont(SELECTED_ITEM_FONT); - - // drawing text - auto name = *itemBlock.tree.node->name() != 0 ? itemBlock.tree.node->name() : easyDescriptor(itemBlock.tree.node->id()).name(); - _painter->drawText(rect, flags, ::profiler_gui::toUnicode(name)); - - // restore previous pen color - if (previousPenStyle == Qt::NoPen) - _painter->setPen(Qt::NoPen); - else - _painter->setPen(BORDERS_COLOR & inverseColor);// BORDERS_COLOR); // restore pen for rectangle painting - - // restore font - if (item.block == EASY_GLOBALS.selected_block) - _painter->setFont(ITEMS_FONT); - // END Draw text~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - } - } - - if (EASY_GLOBALS.selected_block < EASY_GLOBALS.gui_blocks.size()) - { - const auto& guiblock = EASY_GLOBALS.gui_blocks[EASY_GLOBALS.selected_block]; - if (guiblock.graphics_item == m_index) - { - const auto& item = m_levels[guiblock.graphics_item_level][guiblock.graphics_item_index]; - if (item.left() < sceneRight && item.right() > sceneLeft) - { - auto top = levelY(guiblock.graphics_item_level); - auto w = ::std::max(item.width() * currentScale, 1.0); - decltype(top) h = (selectedItemsWasPainted && easyBlock(item.block).expanded && w > 20) ? GRAPHICS_ROW_SIZE : item.totalHeight; - - auto dh = top + h - visibleBottom; - if (dh < h) - { + //x = item.left() * currentScale - dx; + h = item.totalHeight; + const auto dh = top + h - visibleBottom; if (dh > 0) h -= dh; - QPen pen(Qt::SolidLine); - pen.setJoinStyle(Qt::MiterJoin); - pen.setColor(selectedItemBorderColor(item.color));//Qt::red); - pen.setWidth(3); - _painter->setPen(pen); + if (item.block == EASY_GLOBALS.selected_block) + selectedItemsWasPainted = true; - if (!selectedItemsWasPainted) + const bool colorChange = (previousColor != itemDesc.color()); + if (colorChange) { - brush.setColor(item.color);// SELECTED_ITEM_COLOR); + // Set background color brush for rectangle + previousColor = itemDesc.color(); + inverseColor = 0xffffffff - previousColor; + textColor = ::profiler_gui::textColorForRgb(previousColor); + brush.setColor(previousColor); _painter->setBrush(brush); } - else + + if (EASY_GLOBALS.draw_graphics_items_borders && (previousPenStyle != Qt::SolidLine || colorChange)) { - _painter->setBrush(Qt::NoBrush); + // Restore pen for item which is wide enough to paint borders + previousPenStyle = Qt::SolidLine; + _painter->setPen(BORDERS_COLOR & inverseColor);// BORDERS_COLOR); } - auto x = item.left() * currentScale - dx; + if (w < 2) w = 2; + + // Draw rectangle rect.setRect(x, top, w, h); _painter->drawRect(rect); - if (!selectedItemsWasPainted && w > 20) + prevRight = rect.right(); + skip_children(next_level, item.children_begin); + if (w < 20) + continue; + + if (item.totalHeight > GRAPHICS_ROW_SIZE) + flags = Qt::AlignCenter; + else if (!(item.width() < 1)) + flags = Qt::AlignHCenter; + } + else + { + if (next_level < levelsNumber && item.children_begin != MAX_CHILD_INDEX) { - // Draw text----------------------------------- - // calculating text coordinates - auto xtext = x; - if (item.left() < sceneLeft) + if (m_levelsIndexes[next_level] == MAX_CHILD_INDEX) { - // if item left border is out of screen then attach text to the left border of the screen - // to ensure text is always visible for items presenting on the screen. - w += (item.left() - sceneLeft) * currentScale; - xtext = sceneLeft * currentScale - dx; + // Mark first potentially visible child item on next sublevel + m_levelsIndexes[next_level] = item.children_begin; } - if (item.right() > sceneRight) - { - w -= (item.right() - sceneRight) * currentScale; - } + // Mark children items that we want to draw them + m_levels[next_level][item.children_begin].state = BLOCK_ITEM_DO_PAINT; + } - rect.setRect(xtext + 1, top, w - 1, h); + if (item.block == EASY_GLOBALS.selected_block) + selectedItemsWasPainted = true; - // text will be painted with inverse color - //auto textColor = 0x00ffffff - previousColor; - //if (textColor == previousColor) textColor = 0; - textColor = ::profiler_gui::textColorForRgb(item.color);// SELECTED_ITEM_COLOR); - _painter->setPen(textColor); + const bool colorChange = (previousColor != itemDesc.color()); + if (colorChange) + { + // Set background color brush for rectangle + previousColor = itemDesc.color(); + inverseColor = 0xffffffff - previousColor; + textColor = ::profiler_gui::textColorForRgb(previousColor); + brush.setColor(previousColor); + _painter->setBrush(brush); + } - _painter->setFont(SELECTED_ITEM_FONT); + if (EASY_GLOBALS.draw_graphics_items_borders && (previousPenStyle != Qt::SolidLine || colorChange)) + { + // Restore pen for item which is wide enough to paint borders + previousPenStyle = Qt::SolidLine; + _painter->setPen(BORDERS_COLOR & inverseColor);// BORDERS_COLOR); + } + + // Draw rectangle + //x = item.left() * currentScale - dx; + h = GRAPHICS_ROW_SIZE; + const auto dh = top + h - visibleBottom; + if (dh > 0) + h -= dh; + + rect.setRect(x, top, w, h); + _painter->drawRect(rect); + + if (!(item.width() < 1)) + flags = Qt::AlignHCenter; + + prevRight = rect.right(); + } + + // Draw text----------------------------------- + // calculating text coordinates + auto xtext = x; + if (item.left() < sceneLeft) + { + // if item left border is out of screen then attach text to the left border of the screen + // to ensure text is always visible for items presenting on the screen. + w += (item.left() - sceneLeft) * currentScale; + xtext = sceneLeft * currentScale - dx; + } + + if (item.right() > sceneRight) + { + w -= (item.right() - sceneRight) * currentScale; + } + + rect.setRect(xtext + 1, top, w - 1, h); + + // text will be painted with inverse color + //auto textColor = inverseColor < 0x00808080 ? profiler::colors::Black : profiler::colors::White; + //if (textColor == previousColor) textColor = 0; + _painter->setPen(QColor::fromRgb(textColor)); + + if (item.block == EASY_GLOBALS.selected_block) + _painter->setFont(SELECTED_ITEM_FONT); + + // drawing text + auto name = *itemBlock.tree.node->name() != 0 ? itemBlock.tree.node->name() : itemDesc.name(); + _painter->drawText(rect, flags, ::profiler_gui::toUnicode(name)); + + // restore previous pen color + if (previousPenStyle == Qt::NoPen) + _painter->setPen(Qt::NoPen); + else + _painter->setPen(BORDERS_COLOR & inverseColor);// BORDERS_COLOR); // restore pen for rectangle painting + + // restore font + if (item.block == EASY_GLOBALS.selected_block) + _painter->setFont(ITEMS_FONT); + // END Draw text~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + } + } + + if (EASY_GLOBALS.selected_block < EASY_GLOBALS.gui_blocks.size()) + { + const auto& guiblock = EASY_GLOBALS.gui_blocks[EASY_GLOBALS.selected_block]; + if (guiblock.graphics_item == m_index) + { + const auto& item = m_levels[guiblock.graphics_item_level][guiblock.graphics_item_index]; + if (item.left() < sceneRight && item.right() > sceneLeft) + { + auto top = levelY(guiblock.graphics_item_level); + auto w = ::std::max(item.width() * currentScale, 1.0); + decltype(top) h = (selectedItemsWasPainted && easyBlock(item.block).expanded && w > 20) ? GRAPHICS_ROW_SIZE : item.totalHeight; + + auto dh = top + h - visibleBottom; + if (dh < h) + { + if (dh > 0) + h -= dh; - // drawing text const auto& itemBlock = easyBlock(item.block); - auto name = *itemBlock.tree.node->name() != 0 ? itemBlock.tree.node->name() : easyDescriptor(itemBlock.tree.node->id()).name(); - _painter->drawText(rect, Qt::AlignCenter, ::profiler_gui::toUnicode(name)); - // END Draw text~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const auto& itemDesc = easyDescriptor(itemBlock.tree.node->id()); + + QPen pen(Qt::SolidLine); + pen.setJoinStyle(Qt::MiterJoin); + pen.setColor(selectedItemBorderColor(itemDesc.color()));//Qt::red); + pen.setWidth(3); + _painter->setPen(pen); + + if (!selectedItemsWasPainted) + { + brush.setColor(itemDesc.color());// SELECTED_ITEM_COLOR); + _painter->setBrush(brush); + } + else + { + _painter->setBrush(Qt::NoBrush); + } + + auto x = item.left() * currentScale - dx; + rect.setRect(x, top, w, h); + _painter->drawRect(rect); + + if (!selectedItemsWasPainted && w > 20) + { + // Draw text----------------------------------- + // calculating text coordinates + auto xtext = x; + if (item.left() < sceneLeft) + { + // if item left border is out of screen then attach text to the left border of the screen + // to ensure text is always visible for items presenting on the screen. + w += (item.left() - sceneLeft) * currentScale; + xtext = sceneLeft * currentScale - dx; + } + + if (item.right() > sceneRight) + { + w -= (item.right() - sceneRight) * currentScale; + } + + rect.setRect(xtext + 1, top, w - 1, h); + + // text will be painted with inverse color + //auto textColor = 0x00ffffff - previousColor; + //if (textColor == previousColor) textColor = 0; + textColor = ::profiler_gui::textColorForRgb(itemDesc.color());// SELECTED_ITEM_COLOR); + _painter->setPen(textColor); + + _painter->setFont(SELECTED_ITEM_FONT); + + // drawing text + auto name = *itemBlock.tree.node->name() != 0 ? itemBlock.tree.node->name() : easyDescriptor(itemBlock.tree.node->id()).name(); + _painter->drawText(rect, Qt::AlignCenter, ::profiler_gui::toUnicode(name)); + // END Draw text~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + } } } } @@ -529,47 +533,78 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* } - if (!m_pRoot->sync.empty()) - { - _painter->setBrush(QColor::fromRgb(::profiler::colors::Coral));// 0xfffe6030)); - _painter->setPen(QColor::fromRgb(::profiler::colors::Grey800));// 0x00505050)); + if (gotSync) + { + auto firstSync = ::std::lower_bound(m_pRoot->sync.begin(), m_pRoot->sync.end(), sceneLeft, [&sceneView](::profiler::block_index_t _index, qreal _value) + { + return sceneView->time2position(easyBlock(_index).tree.node->begin()) < _value; + }); + + if (firstSync != m_pRoot->sync.end()) + { + if (firstSync != m_pRoot->sync.begin()) + --firstSync; + } + else if (!m_pRoot->sync.empty()) + { + firstSync = m_pRoot->sync.begin() + m_pRoot->sync.size() - 1; + } + //firstSync = m_pRoot->sync.begin(); + + previousColor = 0; qreal prevRight = -1e100, top = y() - 4, h = 3; if (top + h < visibleBottom) { for (auto it = firstSync, end = m_pRoot->sync.end(); it != end; ++it) { const auto& item = easyBlock(*it).tree; - auto begin = sceneView->time2position(item.node->begin()); + auto left = sceneView->time2position(item.node->begin()); - if (begin > sceneRight) + if (left > sceneRight) break; // This is first totally invisible item. No need to check other items. - decltype(begin) width = sceneView->time2position(item.node->end()) - begin; - auto r = begin + width; - if (r < sceneLeft) // This item is not visible + decltype(left) width = sceneView->time2position(item.node->end()) - left; + if (left + width < sceneLeft) // This item is not visible continue; - begin *= currentScale; - begin -= dx; + left *= currentScale; + left -= dx; width *= currentScale; - r = begin + width; - if (r <= prevRight) // This item is not visible + if (left + width <= prevRight) // This item is not visible continue; - if (begin < prevRight) + if (left < prevRight) { - width -= prevRight - begin; - begin = prevRight; + width -= prevRight - left; + left = prevRight; } if (width < 2) width = 2; - //_painter->drawLine(QLineF(::std::max(begin, prevRight), top, begin + width, top)); - rect.setRect(begin, top, width, h); + const bool self_thread = item.node->id() != 0 && EASY_GLOBALS.profiler_blocks.find(item.node->id()) != EASY_GLOBALS.profiler_blocks.end(); + ::profiler::color_t color = 0; + if (self_thread) + color = ::profiler::colors::Coral; + else if (item.node->id() == 0) + color = ::profiler::colors::Black; + else + color = ::profiler::colors::RedA400; + + if (previousColor != color) + { + previousColor = color; + _painter->setBrush(QColor::fromRgb(color)); + if (color != ::profiler::colors::Black) + _painter->setPen(QColor::fromRgb(0x00808080 & color)); + else + _painter->setPen(QColor::fromRgb(::profiler::colors::Grey800)); + } + + rect.setRect(left, top, width, h); _painter->drawRect(rect); - prevRight = begin + width; + prevRight = left + width; } } } @@ -770,6 +805,56 @@ const ::profiler_gui::EasyBlockItem* EasyGraphicsItem::intersect(const QPointF& return nullptr; } +const ::profiler_gui::EasyBlock* EasyGraphicsItem::intersectEvent(const QPointF& _pos) const +{ + if (m_pRoot->sync.empty()) + { + return nullptr; + } + + const auto top = y() - 6; + + if (top > _pos.y()) + { + return nullptr; + } + + const auto bottom = top + 5; + if (bottom < _pos.y()) + { + return nullptr; + } + + const auto sceneView = view(); + const auto currentScale = view()->scale(); + auto firstSync = ::std::lower_bound(m_pRoot->sync.begin(), m_pRoot->sync.end(), _pos.x(), [&sceneView](::profiler::block_index_t _index, qreal _value) + { + return sceneView->time2position(easyBlock(_index).tree.node->begin()) < _value; + }); + + if (firstSync == m_pRoot->sync.end()) + firstSync = m_pRoot->sync.begin() + m_pRoot->sync.size() - 1; + else if (firstSync != m_pRoot->sync.begin()) + --firstSync; + + for (auto it = firstSync, end = m_pRoot->sync.end(); it != end; ++it) + { + const auto& item = easyBlock(*it); + + const auto left = sceneView->time2position(item.tree.node->begin()) - 2; + if (left > _pos.x()) + break; + + const auto right = sceneView->time2position(item.tree.node->end()) + 2; + if (right < _pos.x()) + continue; + + return &item; + } + + return nullptr; +} + ////////////////////////////////////////////////////////////////////////// void EasyGraphicsItem::setBoundingRect(qreal x, qreal y, qreal w, qreal h) @@ -1223,12 +1308,16 @@ EasyGraphicsView::EasyGraphicsView(QWidget* _parent) , m_scale(1) , m_offset(0) , m_timelineStep(0) + , m_idleTime(0) , m_mouseButtons(Qt::NoButton) , m_pScrollbar(nullptr) , m_chronometerItem(nullptr) , m_chronometerItemAux(nullptr) + , m_csInfoWidget(nullptr) , m_flickerSpeedX(0) , m_flickerSpeedY(0) + , m_flickerCounterX(0) + , m_flickerCounterY(0) , m_bDoubleClick(false) , m_bUpdatingRect(false) , m_bEmpty(true) @@ -1265,6 +1354,8 @@ void EasyGraphicsView::clearSilent() m_flickerTimer.stop(); m_flickerSpeedX = 0; m_flickerSpeedY = 0; + m_flickerCounterX = 0; + m_flickerCounterY = 0; // Clear all items scene()->clear(); @@ -1276,6 +1367,10 @@ void EasyGraphicsView::clearSilent() m_timelineStep = 1; m_offset = 0; // scroll back to the beginning of the scene + m_idleTimer.stop(); + m_idleTime = 0; + m_csInfoWidget = nullptr; + // Reset necessary flags //m_bTest = false; m_bEmpty = true; @@ -1307,27 +1402,29 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr { const auto& t = threadTree.second; - auto timestart = blocksTree(t.children.front()).node->begin(); + auto timestart = m_beginTime; + auto timefinish = finish; + + if (!t.children.empty()) + timestart = blocksTree(t.children.front()).node->begin(); if (!t.sync.empty()) timestart = ::std::min(timestart, blocksTree(t.sync.front()).node->begin()); - const auto timefinish = blocksTree(t.children.back()).node->end(); + if (!t.children.empty()) + timefinish = blocksTree(t.children.back()).node->end(); + if (!t.sync.empty()) + timefinish = ::std::max(timefinish, blocksTree(t.sync.back()).node->end()); if (m_beginTime > timestart) - { m_beginTime = timestart; - } - if (finish < timefinish) - { + if (finish < timefinish) { finish = timefinish; longestTree = threadTree.first; } - if (mainTree == 0 && !strcmp(t.thread_name, "Main")) - { + if (mainTree == 0 && !strcmp(t.name(), "Main")) mainTree = threadTree.first; - } } // Filling scene with items @@ -1345,13 +1442,29 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr const auto& t = threadTree.second; // fill scene with new items - const auto& tree = t.children; - qreal h = 0, x = time2position(blocksTree(tree.front()).node->begin()); + qreal h = 0, x = 0; + + if (!t.children.empty()) + x = time2position(blocksTree(t.children.front()).node->begin()); + else if (!t.sync.empty()) + x = time2position(blocksTree(t.sync.front()).node->begin()); + auto item = new EasyGraphicsItem(static_cast(m_items.size()), t); - item->setLevels(t.depth); + if (t.depth) + item->setLevels(t.depth); item->setPos(0, y); - const auto children_duration = setTree(item, tree, h, y, 0); + qreal children_duration = 0; + + if (!t.children.empty()) + { + children_duration = setTree(item, t.children, h, y, 0); + } + else if (!t.sync.empty()) + { + children_duration = time2position(blocksTree(t.sync.back()).node->end()) - x; + h = GRAPHICS_ROW_SIZE; + } item->setBoundingRect(0, 0, children_duration + x, h); m_items.push_back(item); @@ -1369,8 +1482,6 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr // Calculating scene rect const qreal endX = time2position(finish) + 1500.0; scene()->setSceneRect(0, 0, endX, y + TIMELINE_ROW_SIZE); - //for (auto item : m_items) - // item->setBoundingRect(0, 0, endX, item->boundingRect().height()); // Center view on the beginning of the scene updateVisibleSceneRect(); @@ -1387,7 +1498,6 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr scene()->addItem(indicator); // Setting flags - //m_bTest = false; m_bEmpty = false; scaleTo(BASE_SCALE); @@ -1396,7 +1506,9 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr emit treeChanged(); if (mainThreadItem != nullptr) + { longestItem = mainThreadItem; + } if (longestItem != nullptr) { @@ -1404,6 +1516,8 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr EASY_GLOBALS.selected_thread = longestItem->threadId(); emit EASY_GLOBALS.events.selectedThreadChanged(longestItem->threadId()); } + + m_idleTimer.start(IDLE_TIMER_INTERVAL); } const EasyGraphicsView::Items &EasyGraphicsView::getItems() const @@ -1492,7 +1606,6 @@ qreal EasyGraphicsView::setTree(EasyGraphicsItem* _item, const ::profiler::Block } b.block = child_index;// &child; - b.color = EASY_GLOBALS.descriptors[child.node->id()]->color();// ::profiler_gui::fromProfilerRgb(::profiler::colors::get_red(color), ::profiler::colors::get_green(color), ::profiler::colors::get_blue(color)); b.setPos(xbegin, duration); b.totalHeight = GRAPHICS_ROW_SIZE + h; b.state = BLOCK_ITEM_UNCHANGED; @@ -1600,6 +1713,8 @@ void EasyGraphicsView::scaleTo(qreal _scale) void EasyGraphicsView::wheelEvent(QWheelEvent* _event) { + m_idleTime = 0; + if (!m_bEmpty) onWheel(mapToScene(_event->pos()).x(), _event->delta()); _event->accept(); @@ -1607,6 +1722,8 @@ void EasyGraphicsView::wheelEvent(QWheelEvent* _event) void EasyGraphicsView::onGraphicsScrollbarWheel(qreal _mouseX, int _wheelDelta) { + m_idleTime = 0; + for (auto item : m_items) { if (item->threadId() == EASY_GLOBALS.selected_thread) @@ -1657,6 +1774,8 @@ void EasyGraphicsView::onWheel(qreal _mouseX, int _wheelDelta) void EasyGraphicsView::mousePressEvent(QMouseEvent* _event) { + m_idleTime = 0; + if (m_bEmpty) { _event->accept(); @@ -1680,6 +1799,8 @@ void EasyGraphicsView::mousePressEvent(QMouseEvent* _event) void EasyGraphicsView::mouseDoubleClickEvent(QMouseEvent* _event) { + m_idleTime = 0; + if (m_bEmpty) { _event->accept(); @@ -1706,6 +1827,8 @@ void EasyGraphicsView::mouseDoubleClickEvent(QMouseEvent* _event) void EasyGraphicsView::mouseReleaseEvent(QMouseEvent* _event) { + m_idleTime = 0; + if (m_bEmpty) { _event->accept(); @@ -1867,6 +1990,8 @@ bool EasyGraphicsView::moveChrono(EasyChronometerItem* _chronometerItem, qreal _ void EasyGraphicsView::mouseMoveEvent(QMouseEvent* _event) { + m_idleTime = 0; + if (m_bEmpty || (m_mouseButtons == 0 && !m_chronometerItem->isVisible())) { _event->accept(); @@ -1922,7 +2047,7 @@ void EasyGraphicsView::mouseMoveEvent(QMouseEvent* _event) // Update flicker speed m_flickerSpeedX += delta.x() >> 1; - m_flickerSpeedY += delta.y() >> 1; + m_flickerSpeedY += delta.y(); if (!m_flickerTimer.isActive()) { // If flicker timer is not started, then start it @@ -1955,6 +2080,7 @@ void EasyGraphicsView::keyPressEvent(QKeyEvent* _event) static const int KeyStep = 100; const int key = _event->key(); + m_idleTime = 0; switch (key) { @@ -2009,6 +2135,7 @@ void EasyGraphicsView::keyPressEvent(QKeyEvent* _event) void EasyGraphicsView::keyReleaseEvent(QKeyEvent* _event) { const int key = _event->key(); + m_idleTime = 0; m_keys.erase(key); _event->accept(); @@ -2054,6 +2181,7 @@ void EasyGraphicsView::initMode() setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); connect(verticalScrollBar(), &QScrollBar::valueChanged, this, &This::onScrollbarValueChange); connect(&m_flickerTimer, &QTimer::timeout, this, &This::onFlickerTimeout); + connect(&m_idleTimer, &QTimer::timeout, this, &This::onIdleTimeout); auto globalSignals = &EASY_GLOBALS.events; connect(globalSignals, &::profiler_gui::EasyGlobalSignals::selectedThreadChanged, this, &This::onSelectedThreadChange); @@ -2088,6 +2216,9 @@ void EasyGraphicsView::onGraphicsScrollbarValueChange(qreal _value) void EasyGraphicsView::onFlickerTimeout() { + ++m_flickerCounterX; + ++m_flickerCounterY; + if (m_mouseButtons & Qt::LeftButton) { // Fast slow-down and stop if mouse button is pressed, no flicking. @@ -2112,14 +2243,108 @@ void EasyGraphicsView::onFlickerTimeout() updateVisibleSceneRect(); // Update scene visible rect only once repaintScene(); // repaint scene - m_flickerSpeedX -= absmin(sign(m_flickerSpeedX), m_flickerSpeedX); - m_flickerSpeedY -= absmin(sign(m_flickerSpeedY), m_flickerSpeedY); + const int dx = static_cast(sign(m_flickerSpeedX) * m_flickerCounterX / FLICKER_FACTOR); + const int dy = static_cast(sign(m_flickerSpeedY) * m_flickerCounterY / FLICKER_FACTOR); + + if (abs(dx) > 0) + { + m_flickerSpeedX -= absmin(dx, m_flickerSpeedX); + m_flickerCounterX = 0; + } + + if (abs(dy) > 0) + { + m_flickerSpeedY -= absmin(dy, m_flickerSpeedY); + m_flickerCounterY = 0; + } } if (m_flickerSpeedX == 0 && m_flickerSpeedY == 0) { // Flicker stopped, no timer needed. m_flickerTimer.stop(); + m_flickerSpeedX = 0; + m_flickerSpeedY = 0; + m_flickerCounterX = 0; + m_flickerCounterY = 0; + } +} + +////////////////////////////////////////////////////////////////////////// + +void EasyGraphicsView::onIdleTimeout() +{ + m_idleTime += IDLE_TIMER_INTERVAL; + + if (m_idleTime > IDLE_TIME) + { + if (m_csInfoWidget != nullptr) + return; + + auto scenePos = mapToScene(mapFromGlobal(QCursor::pos())); + decltype(scenePos) pos(m_offset + scenePos.x() / m_scale, scenePos.y()); + + // Try to select one of context switches or items + for (auto item : m_items) + { + auto block = item->intersect(pos); + if (block) + { + const auto& itemBlock = easyBlock(block->block); + auto name = *itemBlock.tree.node->name() != 0 ? itemBlock.tree.node->name() : easyDescriptor(itemBlock.tree.node->id()).name(); + + auto widget = new QWidget(); + auto lay = new QFormLayout(widget); + lay->setLabelAlignment(Qt::AlignRight); + lay->addRow("Name:", new QLabel(name)); + lay->addRow("Duration:", new QLabel(::profiler_gui::timeStringReal(units2microseconds(block->w), 3))); + + m_csInfoWidget = new QGraphicsProxyWidget(); + m_csInfoWidget->setWidget(widget); + + break; + } + + auto cse = item->intersectEvent(pos); + if (cse) + { + auto widget = new QWidget(); + auto lay = new QFormLayout(widget); + lay->setLabelAlignment(Qt::AlignRight); + + auto it = EASY_GLOBALS.profiler_blocks.find(cse->tree.node->id()); + if (it != EASY_GLOBALS.profiler_blocks.end()) + lay->addRow("Thread:", new QLabel(QString("%1 %2").arg(cse->tree.node->id()).arg(it->second.name()))); + else + lay->addRow("Thread:", new QLabel(QString::number(cse->tree.node->id()))); + lay->addRow("Process:", new QLabel(cse->tree.node->name())); + lay->addRow("Duration:", new QLabel(::profiler_gui::timeStringReal(PROF_MICROSECONDS(cse->tree.node->duration()), 3))); + + m_csInfoWidget = new QGraphicsProxyWidget(); + m_csInfoWidget->setWidget(widget); + + break; + } + } + + if (m_csInfoWidget != nullptr) + { + scene()->addItem(m_csInfoWidget); + + auto br = m_csInfoWidget->boundingRect(); + if (scenePos.y() + br.height() > m_visibleSceneRect.bottom()) + scenePos.setY(scenePos.y() - br.height()); + if (scenePos.x() + br.width() > m_visibleSceneRect.right()) + scenePos.setX(scenePos.x() - br.width()); + + m_csInfoWidget->setPos(scenePos); + m_csInfoWidget->setOpacity(0.85); + } + } + else if (m_csInfoWidget != nullptr) + { + scene()->removeItem(m_csInfoWidget); + m_csInfoWidget = nullptr; } } diff --git a/profiler_gui/blocks_graphics_view.h b/profiler_gui/blocks_graphics_view.h index bbf43b7..5169553 100644 --- a/profiler_gui/blocks_graphics_view.h +++ b/profiler_gui/blocks_graphics_view.h @@ -161,6 +161,7 @@ public: void getBlocks(qreal _left, qreal _right, ::profiler_gui::TreeBlocks& _blocks) const; const ::profiler_gui::EasyBlockItem* intersect(const QPointF& _pos) const; + const ::profiler_gui::EasyBlock* intersectEvent(const QPointF& _pos) const; private: @@ -272,6 +273,8 @@ EASY_QGRAPHICSITEM(EasyTimelineIndicatorItem); ////////////////////////////////////////////////////////////////////////// +class QGraphicsProxyWidget; + class EasyGraphicsView : public QGraphicsView { Q_OBJECT @@ -287,19 +290,24 @@ private: Keys m_keys; ///< Pressed keys ::profiler_gui::TreeBlocks m_selectedBlocks; ///< Array of items which were selected by selection zone (EasyChronometerItem) QTimer m_flickerTimer; ///< Timer for flicking behavior + QTimer m_idleTimer; ///< QRectF m_visibleSceneRect; ///< Visible scene rectangle ::profiler::timestamp_t m_beginTime; ///< Begin time of profiler session. Used to reduce values of all begin and end times of profiler blocks. qreal m_scale; ///< Current scale qreal m_offset; ///< Have to use manual offset for all scene content instead of using scrollbars because QScrollBar::value is 32-bit integer :( qreal m_timelineStep; ///< + uint64_t m_idleTime; ///< QPoint m_mousePressPos; ///< Last mouse global position (used by mousePressEvent and mouseMoveEvent) QPoint m_mouseMovePath; ///< Mouse move path between press and release of any button Qt::MouseButtons m_mouseButtons; ///< Pressed mouse buttons EasyGraphicsScrollbar* m_pScrollbar; ///< Pointer to the graphics scrollbar widget EasyChronometerItem* m_chronometerItem; ///< Pointer to the EasyChronometerItem which is displayed when you press right mouse button and move mouse left or right. This item is used to select blocks to display in tree widget. EasyChronometerItem* m_chronometerItemAux; ///< Pointer to the EasyChronometerItem which is displayed when you double click left mouse button and move mouse left or right. This item is used only to measure time. + QGraphicsProxyWidget* m_csInfoWidget; ///< int m_flickerSpeedX; ///< Current flicking speed x int m_flickerSpeedY; ///< Current flicking speed y + int m_flickerCounterX; + int m_flickerCounterY; bool m_bDoubleClick; ///< Is mouse buttons double clicked bool m_bUpdatingRect; ///< Stub flag which is used to avoid excess calculations on some scene update (flicking, scaling and so on) bool m_bEmpty; ///< Indicates whether scene is empty and has no items @@ -361,6 +369,7 @@ private slots: void onScrollbarValueChange(int); void onGraphicsScrollbarValueChange(qreal); void onFlickerTimeout(); + void onIdleTimeout(); void onSelectedThreadChange(::profiler::thread_id_t _id); void onSelectedBlockChange(unsigned int _block_index); void onItemsEspandStateChange(); diff --git a/profiler_gui/common_types.h b/profiler_gui/common_types.h index 976761e..ea561fa 100644 --- a/profiler_gui/common_types.h +++ b/profiler_gui/common_types.h @@ -138,7 +138,6 @@ struct EasyBlockItem final //const ::profiler::BlocksTree* block; ///< Pointer to profiler block qreal x; ///< x coordinate of the item (this is made qreal=double to avoid mistakes on very wide scene) float w; ///< Width of the item - QRgb color; ///< Background color of the item ::profiler::block_index_t block; ///< Index of profiler block uint32_t children_begin; ///< Index of first child item on the next sublevel uint16_t totalHeight; ///< Total height of the item including heights of all it's children diff --git a/profiler_gui/graphics_scrollbar.cpp b/profiler_gui/graphics_scrollbar.cpp index 753e631..0b722da 100644 --- a/profiler_gui/graphics_scrollbar.cpp +++ b/profiler_gui/graphics_scrollbar.cpp @@ -515,9 +515,9 @@ void EasyGraphicsScrollbar::contextMenuEvent(QContextMenuEvent* _event) for (const auto& it : EASY_GLOBALS.profiler_blocks) { QString label; - if (it.second.thread_name && it.second.thread_name[0] != 0) + if (it.second.gotName()) { - label = ::std::move(QString("%1 Thread %2").arg(it.second.thread_name).arg(it.first)); + label = ::std::move(QString("%1 Thread %2").arg(it.second.name()).arg(it.first)); } else { diff --git a/profiler_gui/tree_widget_loader.cpp b/profiler_gui/tree_widget_loader.cpp index a0076e7..bef9537 100644 --- a/profiler_gui/tree_widget_loader.cpp +++ b/profiler_gui/tree_widget_loader.cpp @@ -161,20 +161,14 @@ void FillTreeClass::setTreeInternal1(T& _safelocker, Items& _items, ThreadedI const auto& root = threadTree.second; auto item = new EasyTreeWidgetItem(); - if (root.thread_name && root.thread_name[0] != 0) - { - item->setText(COL_NAME, QString("%1 Thread %2").arg(root.thread_name).arg(root.thread_id)); - } + if (root.gotName()) + item->setText(COL_NAME, QString("%1 Thread %2").arg(root.name()).arg(root.thread_id)); else - { item->setText(COL_NAME, QString("Thread %1").arg(root.thread_id)); - } ::profiler::timestamp_t duration = 0; if (!root.children.empty()) - { duration = blocksTree(root.children.back()).node->end() - blocksTree(root.children.front()).node->begin(); - } item->setTimeSmart(COL_DURATION, duration); item->setBackgroundColor(::profiler_gui::SELECTED_THREAD_BACKGROUND); @@ -259,19 +253,13 @@ void FillTreeClass::setTreeInternal2(T& _safelocker, Items& _items, ThreadedI { thread_item = new EasyTreeWidgetItem(); - if (block.root->thread_name && block.root->thread_name[0] != 0) - { - thread_item->setText(COL_NAME, QString("%1 Thread %2").arg(block.root->thread_name).arg(block.root->thread_id)); - } + if (block.root->gotName()) + thread_item->setText(COL_NAME, QString("%1 Thread %2").arg(block.root->name()).arg(block.root->thread_id)); else - { thread_item->setText(COL_NAME, QString("Thread %1").arg(block.root->thread_id)); - } if (!block.root->children.empty()) - { duration = blocksTree(block.root->children.back()).node->end() - blocksTree(block.root->children.front()).node->begin(); - } thread_item->setTimeSmart(COL_DURATION, duration); thread_item->setBackgroundColor(::profiler_gui::SELECTED_THREAD_BACKGROUND); diff --git a/include/profiler/event_trace_status.h b/src/event_trace_status.h similarity index 100% rename from include/profiler/event_trace_status.h rename to src/event_trace_status.h diff --git a/src/event_trace_win.cpp b/src/event_trace_win.cpp index 5550ba1..21f4ba0 100644 --- a/src/event_trace_win.cpp +++ b/src/event_trace_win.cpp @@ -3,7 +3,9 @@ #ifdef _WIN32 #include #include +#include #include "event_trace_win.h" +#include "Psapi.h" #include "profiler/profiler.h" #include "profile_manager.h" @@ -15,6 +17,16 @@ namespace profiler { + ////////////////////////////////////////////////////////////////////////// + + struct ProcessInfo final { + std::string name; + uint32_t id = 0; + int8_t valid = 0; + }; + + ////////////////////////////////////////////////////////////////////////// + // CSwitch class // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa964744(v=vs.85).aspx // EventType = 36 @@ -34,6 +46,17 @@ namespace profiler { uint32_t Reserved; }; + ////////////////////////////////////////////////////////////////////////// + + typedef ::std::unordered_map thread_process_info_map; + typedef ::std::unordered_map process_info_map; + + // Using static is safe because processTraceEvent() is called from one thread + static process_info_map PROCESS_INFO_TABLE; + static thread_process_info_map THREAD_PROCESS_INFO_TABLE = ([](){ thread_process_info_map initial; initial[0U] = nullptr; return ::std::move(initial); })(); + + ////////////////////////////////////////////////////////////////////////// + void WINAPI processTraceEvent(PEVENT_RECORD _traceEvent) { static const decltype(_traceEvent->EventHeader.EventDescriptor.Opcode) SWITCH_CONTEXT_OPCODE = 36; @@ -43,13 +66,79 @@ namespace profiler { if (sizeof(CSwitch) != _traceEvent->UserDataLength) return; - //EASY_FUNCTION(::profiler::colors::White); + EASY_FUNCTION(::profiler::colors::White, ::profiler::DISABLED); auto _contextSwitchEvent = reinterpret_cast(_traceEvent->UserData); const auto time = static_cast<::profiler::timestamp_t>(_traceEvent->EventHeader.TimeStamp.QuadPart); - static const auto& desc = MANAGER.addBlockDescriptor(true, "OS.ContextSwitch", __FILE__, __LINE__, ::profiler::BLOCK_TYPE_CONTEXT_SWITCH, ::profiler::colors::White); - MANAGER.beginContextSwitch(_contextSwitchEvent->OldThreadId, time, desc.id()); + ProcessInfo* pinfo = nullptr; + auto it = THREAD_PROCESS_INFO_TABLE.find(_contextSwitchEvent->NewThreadId); + if (it == THREAD_PROCESS_INFO_TABLE.end()) + { + auto hThread = OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, _contextSwitchEvent->NewThreadId); + if (hThread != nullptr) + { + auto pid = GetProcessIdOfThread(hThread); + pinfo = &PROCESS_INFO_TABLE[pid]; + + if (pinfo->valid == 0) + { + // According to documentation, using GetModuleBaseName() requires + // PROCESS_QUERY_INFORMATION | PROCESS_VM_READ access rights. + // But it works fine with PROCESS_QUERY_LIMITED_INFORMATION instead of PROCESS_QUERY_INFORMATION. + // + // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683196(v=vs.85).aspx + auto hProc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, FALSE, pid); + if (hProc != nullptr) + { + static TCHAR buf[MAX_PATH] = {}; // Using static is safe because processTraceEvent() is called from one thread + auto success = GetModuleBaseName(hProc, 0, buf, MAX_PATH); + + if (success) + { + static char numbuf[128] = {}; + sprintf(numbuf, "%u ", pid); + + pinfo->name = numbuf; + pinfo->name += buf; + pinfo->id = pid; + pinfo->valid = 1; + //printf("PROCESS %u is %s\n", pid, buf); + } + + CloseHandle(hProc); + } + else + { + pinfo->valid = -1; + } + } + + if (pinfo->valid > 0) + { + THREAD_PROCESS_INFO_TABLE[_contextSwitchEvent->NewThreadId] = pinfo; + } + else if (pinfo->valid < 0) + { + pinfo = nullptr; + THREAD_PROCESS_INFO_TABLE[_contextSwitchEvent->NewThreadId] = nullptr; + } + + CloseHandle(hThread); + } + else + { + THREAD_PROCESS_INFO_TABLE[_contextSwitchEvent->NewThreadId] = nullptr; + } + } + else + { + pinfo = it->second; + if (pinfo != nullptr && pinfo->valid < 0) + pinfo = nullptr; + } + + MANAGER.beginContextSwitch(_contextSwitchEvent->OldThreadId, time, _contextSwitchEvent->NewThreadId, pinfo ? pinfo->name.c_str() : ""); MANAGER.endContextSwitch(_contextSwitchEvent->NewThreadId, time); } @@ -109,6 +198,7 @@ namespace profiler { { static const decltype(m_properties.base.Wnode.ClientContext) RAW_TIMESTAMP_TIME_TYPE = 1; + profiler::guard_lock lock(m_spin); if (m_bEnabled) return EVENT_TRACING_LAUNCHED_SUCCESSFULLY; @@ -142,12 +232,15 @@ namespace profiler { // the controller stops the trace session. (Note that there may be a several-second delay before the function returns.) // // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364093(v=vs.85).aspx - m_stubThread = ::std::move(::std::thread([this]() + m_processThread = ::std::move(::std::thread([this]() { EASY_THREAD("EasyProfiler.ETW"); ProcessTrace(&m_openedHandle, 1, 0, 0); })); + // Set low priority for event tracing thread + SetThreadPriority(m_processThread.native_handle(), THREAD_PRIORITY_LOWEST); + m_bEnabled = true; return EVENT_TRACING_LAUNCHED_SUCCESSFULLY; @@ -155,17 +248,23 @@ namespace profiler { void EasyEventTracer::disable() { + profiler::guard_lock lock(m_spin); if (!m_bEnabled) return; ControlTrace(m_openedHandle, KERNEL_LOGGER_NAME, props(), EVENT_TRACE_CONTROL_STOP); CloseTrace(m_openedHandle); - // Wait for ProcessThread to finish - if (m_stubThread.joinable()) - m_stubThread.join(); + // Wait for ProcessTrace to finish to make sure no processTraceEvent() will be called later. + if (m_processThread.joinable()) + m_processThread.join(); m_bEnabled = false; + + // processTraceEvent() is not called anymore. Clean static maps is safe. + PROCESS_INFO_TABLE.clear(); + THREAD_PROCESS_INFO_TABLE.clear(); + THREAD_PROCESS_INFO_TABLE[0U] = nullptr; } } // END of namespace profiler. diff --git a/src/event_trace_win.h b/src/event_trace_win.h index a8aa11a..731c9ed 100644 --- a/src/event_trace_win.h +++ b/src/event_trace_win.h @@ -12,7 +12,8 @@ #include #include #include -#include "profiler/event_trace_status.h" +#include "event_trace_status.h" +#include "spin_lock.h" ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// @@ -29,12 +30,13 @@ namespace profiler { }; #pragma pack(pop) - ::std::thread m_stubThread; - Properties m_properties; - EVENT_TRACE_LOGFILE m_trace; - TRACEHANDLE m_sessionHandle = INVALID_PROCESSTRACE_HANDLE; - TRACEHANDLE m_openedHandle = INVALID_PROCESSTRACE_HANDLE; - bool m_bEnabled = false; + ::std::thread m_processThread; + Properties m_properties; + EVENT_TRACE_LOGFILE m_trace; + profiler::spin_lock m_spin; + TRACEHANDLE m_sessionHandle = INVALID_PROCESSTRACE_HANDLE; + TRACEHANDLE m_openedHandle = INVALID_PROCESSTRACE_HANDLE; + bool m_bEnabled = false; public: diff --git a/src/hashed_cstr.h b/src/hashed_cstr.h index 0691c34..b9896d9 100644 --- a/src/hashed_cstr.h +++ b/src/hashed_cstr.h @@ -64,7 +64,11 @@ namespace profiler { public: - explicit cstring(const char* _str) : m_str(_str), m_len(strlen(_str)) + cstring(const char* _str) : m_str(_str), m_len(strlen(_str)) + { + } + + cstring(const char* _str, size_t _len) : m_str(_str), m_len(_len) { } @@ -91,6 +95,16 @@ namespace profiler { return m_len < _other.m_len; } + inline const char* c_str() const + { + return m_str; + } + + inline size_t size() const + { + return m_len; + } + }; // END of class cstring. /** \brief cstring with precalculated hash. @@ -110,11 +124,19 @@ namespace profiler { public: - explicit hashed_cstr(const char* _str) : Parent(_str), m_hash(0) + hashed_cstr(const char* _str) : Parent(_str), m_hash(0) { m_hash = ::std::_Hash_seq((const unsigned char *)m_str, m_len); } + hashed_cstr(const char* _str, size_t _hash_code) : Parent(_str), m_hash(_hash_code) + { + } + + hashed_cstr(const char* _str, size_t _len, size_t _hash_code) : Parent(_str, _len), m_hash(_hash_code) + { + } + hashed_cstr(const hashed_cstr&) = default; hashed_cstr& operator = (const hashed_cstr&) = default; @@ -206,6 +228,16 @@ namespace profiler { return m_hash; } + inline const char* c_str() const + { + return m_str.c_str(); + } + + inline size_t size() const + { + return m_str.size(); + } + }; // END of class hashed_stdstring. } // END of namespace profiler. diff --git a/src/profile_manager.cpp b/src/profile_manager.cpp index 935e2c7..1c65f2d 100644 --- a/src/profile_manager.cpp +++ b/src/profile_manager.cpp @@ -47,9 +47,9 @@ extern timestamp_t getCurrentTime(); extern "C" { - PROFILER_API const BaseBlockDescriptor& registerDescription(bool _enabled, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color) + PROFILER_API const BaseBlockDescriptor& registerDescription(bool _enabled, const char* _autogenUniqueId, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color) { - return MANAGER.addBlockDescriptor(_enabled, _name, _filename, _line, _block_type, _color); + return MANAGER.addBlockDescriptor(_enabled, _autogenUniqueId, _name, _filename, _line, _block_type, _color); } PROFILER_API void endBlock() @@ -60,15 +60,9 @@ extern "C" { PROFILER_API void setEnabled(bool isEnable) { MANAGER.setEnabled(isEnable); -#ifdef _WIN32 - if (isEnable) - EasyEventTracer::instance().enable(true); - else - EasyEventTracer::instance().disable(); -#endif } - PROFILER_API void storeBlock(const BaseBlockDescriptor& _desc, const char* _runtimeName) + PROFILER_API void storeEvent(const BaseBlockDescriptor& _desc, const char* _runtimeName) { MANAGER.storeBlock(_desc, _runtimeName); } @@ -83,11 +77,12 @@ extern "C" { return MANAGER.dumpBlocksToFile(filename); } - PROFILER_API const char* setThreadName(const char* name, const char* filename, const char* _funcname, int line) + PROFILER_API const char* registerThread(const char* name)//, const char* filename, const char* _funcname, int line) { - return MANAGER.setThreadName(name, filename, _funcname, line); + return MANAGER.registerThread(name);// , filename, _funcname, line); } +#ifndef _WIN32 PROFILER_API void setContextSwitchLogFilename(const char* name) { return MANAGER.setContextSwitchLogFilename(name); @@ -97,6 +92,7 @@ extern "C" { { return MANAGER.getContextSwitchLogFilename(); } +#endif } @@ -124,6 +120,8 @@ BlockDescriptor::BlockDescriptor(uint64_t& _used_mem, block_id_t _id, bool _enab : BaseBlockDescriptor(_id, _enabled, _line, _block_type, _color) , m_name(_name) , m_filename(_filename) + , m_pEnable(nullptr) + , m_expired(false) { _used_mem += sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2; } @@ -132,13 +130,10 @@ BlockDescriptor::BlockDescriptor(uint64_t& _used_mem, bool _enabled, const char* : BaseBlockDescriptor(0, _enabled, _line, _block_type, _color) , m_name(_name) , m_filename(_filename) + , m_pEnable(nullptr) + , m_expired(false) { _used_mem += sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2; -} - -void BlockDescriptor::setId(block_id_t _id) -{ - m_id = _id; } BlockDescRef::~BlockDescRef() @@ -151,7 +146,7 @@ BlockDescRef::~BlockDescRef() void ThreadStorage::storeBlock(const profiler::Block& block) { #if EASY_MEASURE_STORAGE_EXPAND != 0 - static const auto desc = MANAGER.addBlockDescriptor(EASY_STORAGE_EXPAND_ENABLED, "EasyProfiler.ExpandStorage", __FILE__, __LINE__, profiler::BLOCK_TYPE_BLOCK, profiler::colors::White); + static const auto desc = MANAGER.addBlockDescriptor(EASY_STORAGE_EXPAND_ENABLED, EASY_UNIQUE_LINE_ID, "EasyProfiler.ExpandStorage", __FILE__, __LINE__, profiler::BLOCK_TYPE_BLOCK, profiler::colors::White); #endif auto name_length = static_cast(strlen(block.name())); @@ -205,7 +200,8 @@ EASY_THREAD_LOCAL static ::ThreadStorage* THREAD_STORAGE = nullptr; ProfileManager::ProfileManager() { - m_expiredDescriptors.reserve(1024U); + m_isEnabled = ATOMIC_VAR_INIT(false); + m_isEventTracingEnabled = ATOMIC_VAR_INIT(EASY_EVENT_TRACING_ENABLED); } ProfileManager::~ProfileManager() @@ -213,7 +209,8 @@ ProfileManager::~ProfileManager() //dumpBlocksToFile("test.prof"); for (auto desc : m_descriptors) { - delete desc; + if (desc != nullptr) + delete desc; } } @@ -231,7 +228,7 @@ void ProfileManager::markExpired(profiler::block_id_t _id) // We can not delete this descriptor now, because we need to send/write all collected data first. guard_lock_t lock(m_storedSpin); - m_expiredDescriptors.push_back(_id); + m_descriptors[_id]->m_expired = true; } ThreadStorage& ProfileManager::threadStorage(profiler::thread_id_t _thread_id) @@ -240,9 +237,8 @@ ThreadStorage& ProfileManager::threadStorage(profiler::thread_id_t _thread_id) return m_threads[_thread_id]; } -ThreadStorage* ProfileManager::findThreadStorage(profiler::thread_id_t _thread_id) +ThreadStorage* ProfileManager::_findThreadStorage(profiler::thread_id_t _thread_id) { - guard_lock_t lock(m_spin); auto it = m_threads.find(_thread_id); return it != m_threads.end() ? &it->second : nullptr; } @@ -272,19 +268,21 @@ void ProfileManager::beginBlock(Block& _block) THREAD_STORAGE->blocks.openedList.emplace(_block); } -void ProfileManager::beginContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id) +void ProfileManager::beginContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::thread_id_t _target_thread_id, const char* _target_process, bool _lockSpin) { - auto ts = findThreadStorage(_thread_id); + auto ts = _lockSpin ? findThreadStorage(_thread_id) : _findThreadStorage(_thread_id); if (ts != nullptr) - ts->sync.openedList.emplace(_time, _id, ""); + // Dirty hack: _target_thread_id will be written to the field "block_id_t m_id" + // and will be available calling method id(). + ts->sync.openedList.emplace(_time, _target_thread_id, _target_process); } -void ProfileManager::storeContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id) +void ProfileManager::storeContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::thread_id_t _target_thread_id, bool _lockSpin) { - auto ts = findThreadStorage(_thread_id); + auto ts = _lockSpin ? findThreadStorage(_thread_id) : _findThreadStorage(_thread_id); if (ts != nullptr) { - profiler::Block b(_time, _id, ""); + profiler::Block b(_time, _target_thread_id, ""); b.finish(_time); ts->storeCSwitch(b); } @@ -309,9 +307,9 @@ void ProfileManager::endBlock() THREAD_STORAGE->blocks.openedList.pop(); } -void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime) +void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime, bool _lockSpin) { - auto ts = findThreadStorage(_thread_id); + auto ts = _lockSpin ? findThreadStorage(_thread_id) : _findThreadStorage(_thread_id); if (ts == nullptr || ts->sync.openedList.empty()) return; @@ -325,6 +323,15 @@ void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, profiler void ProfileManager::setEnabled(bool isEnable) { m_isEnabled = isEnable; + +#ifdef _WIN32 + if (isEnable) { + if (m_isEventTracingEnabled) + EasyEventTracer::instance().enable(true); + } else { + EasyEventTracer::instance().disable(); + } +#endif } ////////////////////////////////////////////////////////////////////////// @@ -332,29 +339,37 @@ void ProfileManager::setEnabled(bool isEnable) uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream) { const bool wasEnabled = m_isEnabled; + const bool eventTracingEnabled = m_isEventTracingEnabled; if (wasEnabled) ::profiler::setEnabled(false); + // This is to make sure that no new descriptors or new threads will be + // added until we finish sending data. + guard_lock_t lock1(m_storedSpin); + guard_lock_t lock2(m_spin); + // This is the only place using both spins, so no dead-lock will occur + + #ifndef _WIN32 - // Read thread context switch events from temporary file - - uint64_t timestamp; - uint32_t thread_from, thread_to; - - std::ifstream infile(m_csInfoFilename.c_str()); - - if(infile.is_open()) + if (eventTracingEnabled) { - static const auto& desc = addBlockDescriptor(true, "OS.ContextSwitch", __FILE__, __LINE__, profiler::BLOCK_TYPE_CONTEXT_SWITCH, profiler::colors::White); - while (infile >> timestamp >> thread_from >> thread_to) - { - beginContextSwitch(thread_from, timestamp, desc.id()); - endContextSwitch(thread_to, timestamp); + // Read thread context switch events from temporary file + + uint64_t timestamp = 0; + uint32_t thread_from = 0, thread_to = 0; + + std::ifstream infile(m_csInfoFilename.c_str()); + if(infile.is_open()) { + while (infile >> timestamp >> thread_from >> thread_to) { + beginContextSwitch(thread_from, timestamp, thread_to, "", false); + endContextSwitch(thread_to, timestamp, false); + } } } #endif + // Calculate used memory total size and total blocks number uint64_t usedMemorySize = 0; uint32_t blocks_number = 0; @@ -381,6 +396,9 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream) // Write block descriptors for (const auto descriptor : m_descriptors) { + if (descriptor == nullptr) + continue; + const auto name_size = static_cast(strlen(descriptor->name()) + 1); const auto filename_size = static_cast(strlen(descriptor->file()) + 1); const auto size = static_cast(sizeof(profiler::SerializedBlockDescriptor) + name_size + filename_size); @@ -399,27 +417,30 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream) _outputStream.write(thread_storage.first); + const auto name_size = static_cast(t.name.size() + 1); + _outputStream.write(name_size); + _outputStream.write(name_size > 1 ? t.name.c_str() : "", name_size); + _outputStream.write(t.sync.closedList.size()); t.sync.closedList.serialize(_outputStream); _outputStream.write(t.blocks.closedList.size()); - t.blocks.closedList.serialize(_outputStream); + if (!t.blocks.closedList.empty()) + t.blocks.closedList.serialize(_outputStream); t.clearClosed(); + t.blocks.openedList.clear(); + t.sync.openedList.clear(); } - if (!m_expiredDescriptors.empty()) + // Remove all expired block descriptors (descriptor may become expired if it's .dll/.so have been unloaded during application execution) + for (auto& desc : m_descriptors) { - // Remove all expired block descriptors (descriptor may become expired if it's .dll/.so have been unloaded during application execution) + if (desc == nullptr || !desc->m_expired) + continue; - std::sort(m_expiredDescriptors.begin(), m_expiredDescriptors.end()); - for (auto it = m_expiredDescriptors.rbegin(), rend = m_expiredDescriptors.rend(); it != rend; ++it) - { - auto id = *it; - delete m_descriptors[id]; - m_descriptors.erase(m_descriptors.begin() + id); - } - m_expiredDescriptors.clear(); + delete desc; + desc = nullptr; } //if (wasEnabled) @@ -439,7 +460,7 @@ uint32_t ProfileManager::dumpBlocksToFile(const char* _filename) return blocksNumber; } -const char* ProfileManager::setThreadName(const char* name, const char* filename, const char* _funcname, int line) +const char* ProfileManager::registerThread(const char* name) { if (THREAD_STORAGE == nullptr) { @@ -448,18 +469,31 @@ const char* ProfileManager::setThreadName(const char* name, const char* filename if (!THREAD_STORAGE->named) { - const auto& desc = addBlockDescriptor(true, _funcname, filename, line, profiler::BLOCK_TYPE_THREAD_SIGN, profiler::colors::Black); - - profiler::Block b(desc, name); - b.finish(b.begin()); - - THREAD_STORAGE->storeBlock(b); - THREAD_STORAGE->name = name; THREAD_STORAGE->named = true; + THREAD_STORAGE->name = name; } return THREAD_STORAGE->name.c_str(); } +void ProfileManager::setBlockEnabled(profiler::block_id_t _id, const profiler::hashed_stdstring& _key, bool _enabled) +{ + guard_lock_t lock(m_storedSpin); + + auto desc = m_descriptors[_id]; + if (desc != nullptr) + { + lock.unlock(); + + *desc->m_pEnable = _enabled; + desc->m_enabled = _enabled; + } + else + { + blocks_enable_status_t::key_type key(_key.c_str(), _key.size(), _key.hcode()); + m_blocksEnableStatus[key] = _enabled; + } +} + ////////////////////////////////////////////////////////////////////////// diff --git a/src/profile_manager.h b/src/profile_manager.h index 9e40153..0116763 100644 --- a/src/profile_manager.h +++ b/src/profile_manager.h @@ -22,12 +22,14 @@ along with this program.If not, see . #include "profiler/profiler.h" #include "spin_lock.h" #include "outstream.h" -//#include "hashed_cstr.h" +#include "hashed_cstr.h" #include #include #include +#include #include #include +#include ////////////////////////////////////////////////////////////////////////// @@ -51,7 +53,17 @@ inline uint32_t getCurrentThreadId() #endif } -namespace profiler { class SerializedBlock; } +namespace profiler { + + class SerializedBlock; + + struct do_not_calc_hash { + template inline size_t operator()(T _value) const { + return static_cast(_value); + } + }; + +} ////////////////////////////////////////////////////////////////////////// @@ -149,7 +161,7 @@ public: *(uint16_t*)data = n; data = data + sizeof(uint16_t); - if (m_shift < N) + if (m_shift + 1 < N) *(uint16_t*)(data + n) = 0; return data; @@ -176,6 +188,11 @@ public: return m_size; } + inline bool empty() const + { + return m_size == 0; + } + void clear() { m_size = 0; @@ -201,7 +218,7 @@ public: _outputStream.write((const char*)data, size); data = data + size; i += size; - } while (i < N && *(uint16_t*)data != 0); + } while (i + 1 < N && *(uint16_t*)data != 0); current = current->prev; } while (current != nullptr); @@ -295,20 +312,24 @@ class ProfileManager final typedef profiler::guard_lock guard_lock_t; typedef std::map map_of_threads_stacks; typedef std::vector block_descriptors_t; - typedef std::vector expired_ids_t; + typedef std::unordered_map blocks_enable_status_t; - map_of_threads_stacks m_threads; - block_descriptors_t m_descriptors; - expired_ids_t m_expiredDescriptors; - uint64_t m_usedMemorySize = 0; - profiler::spin_lock m_spin; - profiler::spin_lock m_storedSpin; - profiler::block_id_t m_idCounter = 0; - bool m_isEnabled = false; + map_of_threads_stacks m_threads; + block_descriptors_t m_descriptors; + blocks_enable_status_t m_blocksEnableStatus; + uint64_t m_usedMemorySize = 0; + profiler::spin_lock m_spin; + profiler::spin_lock m_storedSpin; + profiler::block_id_t m_idCounter = 0; + std::atomic_bool m_isEnabled; + std::atomic_bool m_isEventTracingEnabled; +#ifndef _WIN32 std::string m_csInfoFilename = "/tmp/cs_profiling_info.log"; +#endif uint32_t dumpBlocksToStream(profiler::OStream& _outputStream); + void setBlockEnabled(profiler::block_id_t _id, const profiler::hashed_stdstring& _key, bool _enabled); public: @@ -316,14 +337,26 @@ public: ~ProfileManager(); template - const profiler::BaseBlockDescriptor& addBlockDescriptor(bool _enabledByDefault, TArgs ... _args) + const profiler::BaseBlockDescriptor& addBlockDescriptor(bool _enabledByDefault, const char* _autogenUniqueId, TArgs ... _args) { auto desc = new profiler::BlockDescriptor(m_usedMemorySize, _enabledByDefault, _args...); guard_lock_t lock(m_storedSpin); - desc->setId(m_idCounter++); + desc->m_id = m_idCounter++; m_descriptors.emplace_back(desc); + blocks_enable_status_t::key_type key(_autogenUniqueId); + auto it = m_blocksEnableStatus.find(key); + if (it != m_blocksEnableStatus.end()) + { + desc->m_enabled = it->second; + desc->m_pEnable = &it->second; + } + else + { + desc->m_pEnable = &m_blocksEnableStatus.emplace(key, desc->enabled()).first->second; + } + return *desc; } @@ -332,8 +365,9 @@ public: void endBlock(); void setEnabled(bool isEnable); uint32_t dumpBlocksToFile(const char* filename); - const char* setThreadName(const char* name, const char* filename, const char* _funcname, int line); + const char* registerThread(const char* name);// , const char* filename, const char* _funcname, int line); +#ifndef _WIN32 void setContextSwitchLogFilename(const char* name) { m_csInfoFilename = name; @@ -343,16 +377,23 @@ public: { return m_csInfoFilename.c_str(); } +#endif - void beginContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id); - void storeContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id); - void endContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime); + void beginContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::thread_id_t _target_thread_id, const char* _target_process, bool _lockSpin = true); + void storeContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::thread_id_t _target_thread_id, bool _lockSpin = true); + void endContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime, bool _lockSpin = true); private: void markExpired(profiler::block_id_t _id); ThreadStorage& threadStorage(profiler::thread_id_t _thread_id); - ThreadStorage* findThreadStorage(profiler::thread_id_t _thread_id); + ThreadStorage* _findThreadStorage(profiler::thread_id_t _thread_id); + + ThreadStorage* findThreadStorage(profiler::thread_id_t _thread_id) + { + guard_lock_t lock(m_spin); + return _findThreadStorage(_thread_id); + } }; #endif // EASY_PROFILER____MANAGER____H______ diff --git a/src/reader.cpp b/src/reader.cpp index 44c7c94..6318779 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -52,14 +52,6 @@ ////////////////////////////////////////////////////////////////////////// -struct passthrough_hash { - template inline size_t operator () (T _value) const { - return static_cast(_value); - } -}; - -////////////////////////////////////////////////////////////////////////// - namespace profiler { void SerializedData::set(char* _data) @@ -90,7 +82,7 @@ namespace profiler { #ifdef _WIN32 -typedef ::std::unordered_map<::profiler::block_id_t, ::profiler::BlockStatistics*, passthrough_hash> StatsMap; +typedef ::std::unordered_map<::profiler::block_id_t, ::profiler::BlockStatistics*, ::profiler::passthrough_hash> StatsMap; /** \note It is absolutely safe to use hashed_cstr (which simply stores pointer) because std::unordered_map, which uses it as a key, exists only inside fillTreesFromFile function. */ @@ -99,7 +91,7 @@ typedef ::std::unordered_map<::profiler::hashed_cstr, ::profiler::block_id_t> Id #else // TODO: Create optimized version of profiler::hashed_cstr for Linux too. -typedef ::std::unordered_map<::profiler::block_id_t, ::profiler::BlockStatistics*, passthrough_hash> StatsMap; +typedef ::std::unordered_map<::profiler::block_id_t, ::profiler::BlockStatistics*, ::profiler::passthrough_hash> StatsMap; typedef ::std::unordered_map<::profiler::hashed_stdstring, ::profiler::block_id_t> IdMap; #endif @@ -237,10 +229,12 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic& progr progress.store(static_cast(10 * i / descriptors_memory_size)); } - typedef ::std::map<::profiler::thread_id_t, StatsMap> PerThreadStats; + typedef ::std::unordered_map<::profiler::thread_id_t, StatsMap, ::profiler::passthrough_hash> PerThreadStats; PerThreadStats thread_statistics, parent_statistics, frame_statistics; IdMap identification_table; + ::std::vector name; + i = 0; uint32_t read_number = 0; ::profiler::block_index_t blocks_counter = 0; @@ -254,6 +248,15 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic& progr auto& root = threaded_trees[thread_id]; + uint16_t name_size = 0; + inFile.read((char*)&name_size, sizeof(uint16_t)); + if (name_size != 0) + { + name.resize(name_size); + inFile.read(name.data(), name_size); + root.thread_name = name.data(); + } + uint32_t blocks_number_in_thread = 0; inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(blocks_number_in_thread))); auto threshold = read_number + blocks_number_in_thread; @@ -290,10 +293,6 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic& progr tree.node = baseData; const auto block_index = blocks_counter++; - auto descriptor = descriptors[baseData->id()]; - if (descriptor->type() != ::profiler::BLOCK_TYPE_CONTEXT_SWITCH) - continue; - root.sync.emplace_back(block_index); if (progress.load() < 0) @@ -338,18 +337,13 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic& progr blocks.emplace_back(); ::profiler::BlocksTree& tree = blocks.back(); - tree.node = baseData;// new ::profiler::SerializedBlock(sz, data); + tree.node = baseData; const auto block_index = blocks_counter++; auto& per_parent_statistics = parent_statistics[thread_id]; auto& per_thread_statistics = thread_statistics[thread_id]; auto descriptor = descriptors[baseData->id()]; - if (descriptor->type() == ::profiler::BLOCK_TYPE_THREAD_SIGN) - { - root.thread_name = tree.node->name(); - } - if (*tree.node->name() != 0) { // If block has runtime name then generate new id for such block. From bf02f32482a7370d6a719c8b07864b90d21e8378 Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Tue, 13 Sep 2016 23:03:16 +0300 Subject: [PATCH 12/25] small refactoring --- sample/main.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/sample/main.cpp b/sample/main.cpp index c741a24..351812d 100644 --- a/sample/main.cpp +++ b/sample/main.cpp @@ -191,7 +191,6 @@ int main(int argc, char* argv[]) EASY_MAIN_THREAD; std::vector threads; - std::thread render = std::thread(renderThread); std::thread modelling = std::thread(modellingThread); @@ -201,10 +200,10 @@ int main(int argc, char* argv[]) threads.emplace_back(std::thread(renderThread)); threads.emplace_back(std::thread(modellingThread)); } - { - std::lock_guard lk(cv_m); - g_i = 1; - } + + cv_m.lock(); + g_i = 1; + cv_m.unlock(); cv.notify_all(); for (int i = 0; i < RENDER_STEPS; ++i) { @@ -214,9 +213,8 @@ int main(int argc, char* argv[]) render.join(); modelling.join(); - for(auto& t : threads){ + for(auto& t : threads) t.join(); - } /**/ auto end = std::chrono::system_clock::now(); From 313f981cc9e10af1dad3ff8b7ac526952590cadb Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Tue, 13 Sep 2016 23:13:11 +0300 Subject: [PATCH 13/25] Fix CMakeLists.txt --- src/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 63499d2..2d63278 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -10,7 +10,6 @@ set(CPP_FILES set(H_FILES ${ROOT}/include/profiler/profiler.h ${ROOT}/include/profiler/reader.h - ${ROOT}/include/profiler/event_trace_status.h profile_manager.h spin_lock.h event_trace_win.h From fdee7226f897cfcdf8a4393e1b987c587726b89f Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Tue, 13 Sep 2016 23:40:58 +0300 Subject: [PATCH 14/25] (profiler_gui) More information in popup; Right way to remove QGraphicsProxyWidget from scene (it does not delete widget by itself) --- include/profiler/reader.h | 29 +++++++++-------- profiler_gui/blocks_graphics_view.cpp | 47 +++++++++++++++++++++------ profiler_gui/common_types.h | 14 +++++++- profiler_gui/graphics_scrollbar.cpp | 6 +--- profiler_gui/tree_widget_loader.cpp | 33 ++++++++----------- src/reader.cpp | 21 +++++++----- 6 files changed, 92 insertions(+), 58 deletions(-) diff --git a/include/profiler/reader.h b/include/profiler/reader.h index bf35d70..cf61ade 100644 --- a/include/profiler/reader.h +++ b/include/profiler/reader.h @@ -43,14 +43,16 @@ namespace profiler { ::profiler::timestamp_t max_duration; ///< Cached block->duration() value. TODO: Remove this if memory consumption will be too high ::profiler::block_index_t min_duration_block; ///< Will be used in GUI to jump to the block with min duration ::profiler::block_index_t max_duration_block; ///< Will be used in GUI to jump to the block with max duration + ::profiler::block_index_t parent_block; ///< Index of block which is "parent" for "per_parent_stats" or "frame" for "per_frame_stats" or thread-id for "per_thread_stats" ::profiler::calls_number_t calls_number; ///< Block calls number - BlockStatistics(::profiler::timestamp_t _duration, ::profiler::block_index_t _block_index) + explicit BlockStatistics(::profiler::timestamp_t _duration, ::profiler::block_index_t _block_index, ::profiler::block_index_t _parent_index) : total_duration(_duration) , min_duration(_duration) , max_duration(_duration) , min_duration_block(_block_index) , max_duration_block(_block_index) + , parent_block(_parent_index) , calls_number(1) { } @@ -95,12 +97,12 @@ namespace profiler { BlocksTree(This&& that) : BlocksTree() { - makeMove(::std::forward(that)); + make_move(::std::forward(that)); } This& operator = (This&& that) { - makeMove(::std::forward(that)); + make_move(::std::forward(that)); return *this; } @@ -114,9 +116,7 @@ namespace profiler { bool operator < (const This& other) const { if (!node || !other.node) - { return false; - } return node->begin() < other.node->begin(); } @@ -140,7 +140,7 @@ namespace profiler { BlocksTree(const This&) = delete; This& operator = (const This&) = delete; - void makeMove(This&& that) + void make_move(This&& that) { if (per_thread_stats != that.per_thread_stats) release_stats(per_thread_stats); @@ -174,13 +174,14 @@ namespace profiler { public: - BlocksTree::children_t children; - BlocksTree::children_t sync; - std::string thread_name; - ::profiler::thread_id_t thread_id; - uint16_t depth; + BlocksTree::children_t children; ///< List of children indexes + BlocksTree::children_t sync; ///< List of context-switch events + std::string thread_name; ///< Name of this thread + ::profiler::timestamp_t active_time; ///< Active time of this thread (sum of all children duration) + ::profiler::thread_id_t thread_id; ///< System Id of this thread + uint16_t depth; ///< Maximum stack depth (number of levels) - BlocksTreeRoot() : thread_name(""), thread_id(0), depth(0) + BlocksTreeRoot() : thread_name(""), active_time(0), thread_id(0), depth(0) { } @@ -188,6 +189,7 @@ namespace profiler { : children(::std::move(that.children)) , sync(::std::move(that.sync)) , thread_name(::std::move(that.thread_name)) + , active_time(that.active_time) , thread_id(that.thread_id) , depth(that.depth) { @@ -198,12 +200,13 @@ namespace profiler { children = ::std::move(that.children); sync = ::std::move(that.sync); thread_name = ::std::move(that.thread_name); + active_time = that.active_time; thread_id = that.thread_id; depth = that.depth; return *this; } - inline bool gotName() const + inline bool got_name() const { //return thread_name && *thread_name != 0; return thread_name.front() != 0; diff --git a/profiler_gui/blocks_graphics_view.cpp b/profiler_gui/blocks_graphics_view.cpp index a54efa9..acd6f7a 100644 --- a/profiler_gui/blocks_graphics_view.cpp +++ b/profiler_gui/blocks_graphics_view.cpp @@ -150,7 +150,7 @@ inline T logn(T _value) EasyGraphicsItem::EasyGraphicsItem(uint8_t _index, const::profiler::BlocksTreeRoot& _root) : QGraphicsItem(nullptr) - , m_threadName(_root.gotName() ? QString("%1 Thread %2").arg(_root.name()).arg(_root.thread_id) : QString("Thread %1").arg(_root.thread_id)) + , m_threadName(_root.got_name() ? QString("%1 Thread %2").arg(_root.name()).arg(_root.thread_id) : QString("Thread %1").arg(_root.thread_id)) , m_pRoot(&_root) , m_index(_index) { @@ -460,9 +460,10 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* const auto& item = m_levels[guiblock.graphics_item_level][guiblock.graphics_item_index]; if (item.left() < sceneRight && item.right() > sceneLeft) { + const auto& itemBlock = easyBlock(item.block); auto top = levelY(guiblock.graphics_item_level); auto w = ::std::max(item.width() * currentScale, 1.0); - decltype(top) h = (selectedItemsWasPainted && easyBlock(item.block).expanded && w > 20) ? GRAPHICS_ROW_SIZE : item.totalHeight; + decltype(top) h = (selectedItemsWasPainted && itemBlock.expanded && w > 20) ? GRAPHICS_ROW_SIZE : item.totalHeight; auto dh = top + h - visibleBottom; if (dh < h) @@ -470,7 +471,6 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* if (dh > 0) h -= dh; - const auto& itemBlock = easyBlock(item.block); const auto& itemDesc = easyDescriptor(itemBlock.tree.node->id()); QPen pen(Qt::SolidLine); @@ -538,7 +538,7 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* { auto firstSync = ::std::lower_bound(m_pRoot->sync.begin(), m_pRoot->sync.end(), sceneLeft, [&sceneView](::profiler::block_index_t _index, qreal _value) { - return sceneView->time2position(easyBlock(_index).tree.node->begin()) < _value; + return sceneView->time2position(blocksTree(_index).node->begin()) < _value; }); if (firstSync != m_pRoot->sync.end()) @@ -558,7 +558,7 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* { for (auto it = firstSync, end = m_pRoot->sync.end(); it != end; ++it) { - const auto& item = easyBlock(*it).tree; + const auto& item = blocksTree(*it); auto left = sceneView->time2position(item.node->begin()); if (left > sceneRight) @@ -829,7 +829,7 @@ const ::profiler_gui::EasyBlock* EasyGraphicsItem::intersectEvent(const QPointF& const auto currentScale = view()->scale(); auto firstSync = ::std::lower_bound(m_pRoot->sync.begin(), m_pRoot->sync.end(), _pos.x(), [&sceneView](::profiler::block_index_t _index, qreal _value) { - return sceneView->time2position(easyBlock(_index).tree.node->begin()) < _value; + return sceneView->time2position(blocksTree(_index).node->begin()) < _value; }); if (firstSync == m_pRoot->sync.end()) @@ -1357,6 +1357,15 @@ void EasyGraphicsView::clearSilent() m_flickerCounterX = 0; m_flickerCounterY = 0; + if (m_csInfoWidget != nullptr) + { + auto widget = m_csInfoWidget->widget(); + widget->setParent(nullptr); + m_csInfoWidget->setWidget(nullptr); + delete widget; + m_csInfoWidget = nullptr; + } + // Clear all items scene()->clear(); m_items.clear(); @@ -1369,7 +1378,6 @@ void EasyGraphicsView::clearSilent() m_idleTimer.stop(); m_idleTime = 0; - m_csInfoWidget = nullptr; // Reset necessary flags //m_bTest = false; @@ -2290,14 +2298,29 @@ void EasyGraphicsView::onIdleTimeout() auto block = item->intersect(pos); if (block) { - const auto& itemBlock = easyBlock(block->block); - auto name = *itemBlock.tree.node->name() != 0 ? itemBlock.tree.node->name() : easyDescriptor(itemBlock.tree.node->id()).name(); + const auto& itemBlock = blocksTree(block->block); + auto name = *itemBlock.node->name() != 0 ? itemBlock.node->name() : easyDescriptor(itemBlock.node->id()).name(); auto widget = new QWidget(); auto lay = new QFormLayout(widget); lay->setLabelAlignment(Qt::AlignRight); lay->addRow("Name:", new QLabel(name)); - lay->addRow("Duration:", new QLabel(::profiler_gui::timeStringReal(units2microseconds(block->w), 3))); + lay->addRow("Duration:", new QLabel(::profiler_gui::timeStringReal(PROF_MICROSECONDS(itemBlock.node->duration()), 3))); + if (itemBlock.per_thread_stats) + { + lay->addRow("%/Thread:", new QLabel(QString::number(::profiler_gui::percent(itemBlock.per_thread_stats->total_duration, item->root()->active_time)))); + lay->addRow("N/Thread:", new QLabel(QString::number(itemBlock.per_thread_stats->calls_number))); + if (itemBlock.per_parent_stats->parent_block == item->threadId()) + { + auto percent = ::profiler_gui::percentReal(itemBlock.node->duration(), item->root()->active_time); + lay->addRow("%:", new QLabel(percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast(0.5 + percent)))); + } + else + { + auto percent = ::profiler_gui::percentReal(itemBlock.node->duration(), blocksTree(itemBlock.per_parent_stats->parent_block).node->duration()); + lay->addRow("%:", new QLabel(percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast(0.5 + percent)))); + } + } m_csInfoWidget = new QGraphicsProxyWidget(); m_csInfoWidget->setWidget(widget); @@ -2343,6 +2366,10 @@ void EasyGraphicsView::onIdleTimeout() } else if (m_csInfoWidget != nullptr) { + auto widget = m_csInfoWidget->widget(); + widget->setParent(nullptr); + m_csInfoWidget->setWidget(nullptr); + delete widget; scene()->removeItem(m_csInfoWidget); m_csInfoWidget = nullptr; } diff --git a/profiler_gui/common_types.h b/profiler_gui/common_types.h index ea561fa..4c2e3c4 100644 --- a/profiler_gui/common_types.h +++ b/profiler_gui/common_types.h @@ -226,7 +226,7 @@ inline qreal timeFactor(qreal _interval) inline QString timeStringReal(qreal _interval, int _precision = 1) { if (_interval < 1) // interval in nanoseconds - return QString("%1 ns").arg(_interval * 1e3, 0, 'f', _precision); + return QString("%1 ns").arg(static_cast(_interval * 1e3)); if (_interval < 1e3) // interval in microseconds return QString("%1 us").arg(_interval, 0, 'f', _precision); @@ -269,6 +269,18 @@ template inline void set_max(T& _value) { ////////////////////////////////////////////////////////////////////////// +inline double percentReal(::profiler::timestamp_t _partial, ::profiler::timestamp_t _total) +{ + return 100. * static_cast(_partial) / static_cast(_total); +} + +inline int percent(::profiler::timestamp_t _partial, ::profiler::timestamp_t _total) +{ + return static_cast(0.5 + percentReal(_partial, _total)); +} + +////////////////////////////////////////////////////////////////////////// + } // END of namespace profiler_gui. ////////////////////////////////////////////////////////////////////////// diff --git a/profiler_gui/graphics_scrollbar.cpp b/profiler_gui/graphics_scrollbar.cpp index 0b722da..767b2f9 100644 --- a/profiler_gui/graphics_scrollbar.cpp +++ b/profiler_gui/graphics_scrollbar.cpp @@ -515,14 +515,10 @@ void EasyGraphicsScrollbar::contextMenuEvent(QContextMenuEvent* _event) for (const auto& it : EASY_GLOBALS.profiler_blocks) { QString label; - if (it.second.gotName()) - { + if (it.second.got_name()) label = ::std::move(QString("%1 Thread %2").arg(it.second.name()).arg(it.first)); - } else - { label = ::std::move(QString("Thread %1").arg(it.first)); - } auto action = new QAction(label, nullptr); action->setData(it.first); diff --git a/profiler_gui/tree_widget_loader.cpp b/profiler_gui/tree_widget_loader.cpp index bef9537..b93ee68 100644 --- a/profiler_gui/tree_widget_loader.cpp +++ b/profiler_gui/tree_widget_loader.cpp @@ -161,7 +161,7 @@ void FillTreeClass::setTreeInternal1(T& _safelocker, Items& _items, ThreadedI const auto& root = threadTree.second; auto item = new EasyTreeWidgetItem(); - if (root.gotName()) + if (root.got_name()) item->setText(COL_NAME, QString("%1 Thread %2").arg(root.name()).arg(root.thread_id)); else item->setText(COL_NAME, QString("Thread %1").arg(root.thread_id)); @@ -176,13 +176,9 @@ void FillTreeClass::setTreeInternal1(T& _safelocker, Items& _items, ThreadedI //_items.push_back(item); - // TODO: Optimize children duration calculation (it must be calculated before setTreeInternal now) - ::profiler::timestamp_t children_duration = 0; - for (auto i : root.children) - children_duration += blocksTree(i).node->duration(); - item->setTimeSmart(COL_SELF_DURATION, children_duration); + item->setTimeSmart(COL_SELF_DURATION, root.active_time); - children_duration = 0; + ::profiler::timestamp_t children_duration = 0; const auto children_items_number = FillTreeClass::setTreeInternal(_safelocker, _items, _beginTime, root.children, item, nullptr, item, _beginTime, finishtime + 1000000000ULL, false, children_duration, _colorizeRows); if (children_items_number > 0) @@ -253,7 +249,7 @@ void FillTreeClass::setTreeInternal2(T& _safelocker, Items& _items, ThreadedI { thread_item = new EasyTreeWidgetItem(); - if (block.root->gotName()) + if (block.root->got_name()) thread_item->setText(COL_NAME, QString("%1 Thread %2").arg(block.root->name()).arg(block.root->thread_id)); else thread_item->setText(COL_NAME, QString("Thread %1").arg(block.root->thread_id)); @@ -265,11 +261,8 @@ void FillTreeClass::setTreeInternal2(T& _safelocker, Items& _items, ThreadedI thread_item->setBackgroundColor(::profiler_gui::SELECTED_THREAD_BACKGROUND); thread_item->setTextColor(::profiler_gui::SELECTED_THREAD_FOREGROUND); - // Calculate clean duration (sum of all children durations) - ::profiler::timestamp_t children_duration = 0; - for (auto i : block.root->children) - children_duration += blocksTree(i).node->duration(); - thread_item->setTimeSmart(COL_SELF_DURATION, children_duration); + // Sum of all children durations: + thread_item->setTimeSmart(COL_SELF_DURATION, block.root->active_time); threadsMap.insert(::std::make_pair(block.root->thread_id, thread_item)); } @@ -307,7 +300,7 @@ void FillTreeClass::setTreeInternal2(T& _safelocker, Items& _items, ThreadedI item->setData(COL_NCALLS_PER_THREAD, Qt::UserRole, per_thread_stats->calls_number); item->setText(COL_NCALLS_PER_THREAD, QString::number(per_thread_stats->calls_number)); - auto percentage_per_thread = static_cast(0.5 + 100. * static_cast(per_thread_stats->total_duration) / static_cast(thread_item->selfDuration())); + auto percentage_per_thread = ::profiler_gui::percent(per_thread_stats->total_duration, block.root->active_time); item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, percentage_per_thread); item->setText(COL_PERCENT_SUM_PER_THREAD, QString::number(percentage_per_thread)); @@ -455,8 +448,8 @@ size_t FillTreeClass::setTreeInternal(T& _safelocker, Items& _items, const :: const auto& per_parent_stats = child.per_parent_stats; const auto& per_frame_stats = child.per_frame_stats; - auto percentage = duration == 0 ? 0 : static_cast(0.5 + 100. * static_cast(duration) / static_cast(_parent->duration())); - auto percentage_sum = static_cast(0.5 + 100. * static_cast(per_parent_stats->total_duration) / static_cast(_parent->duration())); + auto percentage = duration == 0 ? 0 : ::profiler_gui::percent(duration, _parent->duration()); + auto percentage_sum = ::profiler_gui::percent(per_parent_stats->total_duration, _parent->duration()); item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage); item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage)); item->setData(COL_PERCENT_SUM_PER_PARENT, Qt::UserRole, percentage_sum); @@ -466,8 +459,8 @@ size_t FillTreeClass::setTreeInternal(T& _safelocker, Items& _items, const :: { if (_parent != _frame) { - percentage = duration == 0 ? 0 : static_cast(0.5 + 100. * static_cast(duration) / static_cast(_frame->duration())); - percentage_sum = static_cast(0.5 + 100. * static_cast(per_frame_stats->total_duration) / static_cast(_frame->duration())); + percentage = duration == 0 ? 0 : ::profiler_gui::percent(duration, _frame->duration()); + percentage_sum = ::profiler_gui::percent(per_frame_stats->total_duration, _frame->duration()); } item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, percentage); @@ -497,7 +490,7 @@ size_t FillTreeClass::setTreeInternal(T& _safelocker, Items& _items, const :: if (_thread) { - auto percentage_per_thread = static_cast(0.5 + 100. * static_cast(per_thread_stats->total_duration) / static_cast(_thread->selfDuration())); + auto percentage_per_thread = ::profiler_gui::percent(per_thread_stats->total_duration, _thread->selfDuration()); item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, percentage_per_thread); item->setText(COL_PERCENT_SUM_PER_THREAD, QString::number(percentage_per_thread)); } @@ -558,7 +551,7 @@ size_t FillTreeClass::setTreeInternal(T& _safelocker, Items& _items, const :: auto self_duration = duration - children_duration; if (children_duration > 0 && duration > 0) { - percentage = static_cast(0.5 + 100. * static_cast(self_duration) / static_cast(duration)); + percentage = ::profiler_gui::percent(self_duration, duration); } item->setTimeSmart(COL_SELF_DURATION, self_duration); diff --git a/src/reader.cpp b/src/reader.cpp index 6318779..0830c6b 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -110,7 +110,7 @@ typedef ::std::unordered_map<::profiler::hashed_stdstring, ::profiler::block_id_ automatically receive statistics update. */ -::profiler::BlockStatistics* update_statistics(StatsMap& _stats_map, const ::profiler::BlocksTree& _current, ::profiler::block_index_t _current_index) +::profiler::BlockStatistics* update_statistics(StatsMap& _stats_map, const ::profiler::BlocksTree& _current, ::profiler::block_index_t _current_index, ::profiler::block_index_t _parent_index) { auto duration = _current.node->duration(); //StatsMap::key_type key(_current.node->name()); @@ -146,7 +146,7 @@ automatically receive statistics update. // This is first time the block appear in the file. // Create new statistics. - auto stats = new ::profiler::BlockStatistics(duration, _current_index); + auto stats = new ::profiler::BlockStatistics(duration, _current_index, _parent_index); //_stats_map.emplace(key, stats); _stats_map.emplace(_current.node->id(), stats); @@ -155,12 +155,12 @@ automatically receive statistics update. ////////////////////////////////////////////////////////////////////////// -void update_statistics_recursive(StatsMap& _stats_map, ::profiler::BlocksTree& _current, ::profiler::block_index_t _current_index, ::profiler::blocks_t& _blocks) +void update_statistics_recursive(StatsMap& _stats_map, ::profiler::BlocksTree& _current, ::profiler::block_index_t _current_index, ::profiler::block_index_t _parent_index, ::profiler::blocks_t& _blocks) { - _current.per_frame_stats = update_statistics(_stats_map, _current, _current_index); + _current.per_frame_stats = update_statistics(_stats_map, _current, _current_index, _parent_index); for (auto i : _current.children) { - update_statistics_recursive(_stats_map, _blocks[i], i, _blocks); + update_statistics_recursive(_stats_map, _blocks[i], i, _parent_index, _blocks); } } @@ -397,7 +397,7 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic& progr for (auto i : tree.children) { auto& child = blocks[i]; - child.per_parent_stats = update_statistics(per_parent_statistics, child, i); + child.per_parent_stats = update_statistics(per_parent_statistics, child, i, block_index); children_duration += child.node->duration(); if (tree.depth < child.depth) @@ -426,7 +426,7 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic& progr if (gather_statistics) { EASY_BLOCK("Gather per thread statistics", ::profiler::colors::Coral); - tree.per_thread_stats = update_statistics(per_thread_statistics, tree, block_index); + tree.per_thread_stats = update_statistics(per_thread_statistics, tree, block_index, thread_id); } if (progress.load() < 0) @@ -469,13 +469,15 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic& progr for (auto i : root.children) { auto& frame = blocks[i]; - frame.per_parent_stats = update_statistics(per_parent_statistics, frame, i); + frame.per_parent_stats = update_statistics(per_parent_statistics, frame, i, root.thread_id); per_frame_statistics.clear(); - update_statistics_recursive(per_frame_statistics, frame, i, blocks); + update_statistics_recursive(per_frame_statistics, frame, i, i, blocks); if (root.depth < frame.depth) root.depth = frame.depth; + + root.active_time += frame.node->duration(); } ++root.depth; @@ -508,6 +510,7 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic& progr auto& frame = blocks[i]; if (root.depth < frame.depth) root.depth = frame.depth; + root.active_time += frame.node->duration(); } ++root.depth; From acad6b6b379a104761a7121a0513e80f74d23c08 Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Wed, 14 Sep 2016 21:52:11 +0300 Subject: [PATCH 15/25] (profiler_gui) Added icons and main logo --- profiler_gui/CMakeLists.txt | 2 + profiler_gui/icons/attribution.txt | 4 ++ profiler_gui/icons/logo.svg | 51 ++++++++++++++++++++ profiler_gui/icons/off.svg | 46 ++++++++++++++++++ profiler_gui/icons/open-folder.svg | 41 ++++++++++++++++ profiler_gui/icons/reload.svg | 75 ++++++++++++++++++++++++++++++ profiler_gui/main_window.cpp | 7 ++- profiler_gui/resources.qrc | 8 ++++ 8 files changed, 233 insertions(+), 1 deletion(-) create mode 100644 profiler_gui/icons/attribution.txt create mode 100644 profiler_gui/icons/logo.svg create mode 100644 profiler_gui/icons/off.svg create mode 100644 profiler_gui/icons/open-folder.svg create mode 100644 profiler_gui/icons/reload.svg create mode 100644 profiler_gui/resources.qrc diff --git a/profiler_gui/CMakeLists.txt b/profiler_gui/CMakeLists.txt index 30a2a8f..8a73a86 100644 --- a/profiler_gui/CMakeLists.txt +++ b/profiler_gui/CMakeLists.txt @@ -5,6 +5,7 @@ project(profiler_gui) #set(CMAKE_PREFIX_PATH f:/qt/5.5/5.6/msvc2013_64/lib/cmake) set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) find_package(Qt5Widgets REQUIRED) @@ -30,6 +31,7 @@ add_executable(${PROJECT_NAME} #treemodel.cpp #treeitem.h #treeitem.cpp + resources.qrc ) if(UNIX) diff --git a/profiler_gui/icons/attribution.txt b/profiler_gui/icons/attribution.txt new file mode 100644 index 0000000..0c406e6 --- /dev/null +++ b/profiler_gui/icons/attribution.txt @@ -0,0 +1,4 @@ +logo.svg - Icon made by Freepik from www.flaticon.com +off.svg - Icon made by Freepik from www.flaticon.com +open-folder.svg - Icon made by Freepik from www.flaticon.com +reload.svg - Icon made by Freepik from www.flaticon.com \ No newline at end of file diff --git a/profiler_gui/icons/logo.svg b/profiler_gui/icons/logo.svg new file mode 100644 index 0000000..6c3b090 --- /dev/null +++ b/profiler_gui/icons/logo.svg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/profiler_gui/icons/off.svg b/profiler_gui/icons/off.svg new file mode 100644 index 0000000..7f18abd --- /dev/null +++ b/profiler_gui/icons/off.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/profiler_gui/icons/open-folder.svg b/profiler_gui/icons/open-folder.svg new file mode 100644 index 0000000..767696a --- /dev/null +++ b/profiler_gui/icons/open-folder.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/profiler_gui/icons/reload.svg b/profiler_gui/icons/reload.svg new file mode 100644 index 0000000..fb9034e --- /dev/null +++ b/profiler_gui/icons/reload.svg @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/profiler_gui/main_window.cpp b/profiler_gui/main_window.cpp index 527dda1..e231ddc 100644 --- a/profiler_gui/main_window.cpp +++ b/profiler_gui/main_window.cpp @@ -61,9 +61,11 @@ const int LOADER_TIMER_INTERVAL = 40; EasyMainWindow::EasyMainWindow() : Parent(), m_treeWidget(nullptr), m_graphicsView(nullptr), m_progress(nullptr) { setObjectName("ProfilerGUI_MainWindow"); - setWindowTitle("EasyProfiler Reader v0.2.0"); + setWindowTitle("EasyProfiler Reader beta"); setDockNestingEnabled(true); resize(800, 600); + + { QIcon icon(":/logo"); if (!icon.isNull()) setWindowIcon(icon); } setStatusBar(new QStatusBar()); @@ -90,13 +92,16 @@ EasyMainWindow::EasyMainWindow() : Parent(), m_treeWidget(nullptr), m_graphicsVi auto action = menu->addAction("&Open"); connect(action, &QAction::triggered, this, &This::onOpenFileClicked); + { QIcon icon(":/Open"); if (!icon.isNull()) action->setIcon(icon); } action = menu->addAction("&Reload"); connect(action, &QAction::triggered, this, &This::onReloadFileClicked); + { QIcon icon(":/Reload"); if (!icon.isNull()) action->setIcon(icon); } menu->addSeparator(); action = menu->addAction("&Exit"); connect(action, &QAction::triggered, this, &This::onExitClicked); + { QIcon icon(":/Exit"); if (!icon.isNull()) action->setIcon(icon); } menuBar()->addMenu(menu); diff --git a/profiler_gui/resources.qrc b/profiler_gui/resources.qrc new file mode 100644 index 0000000..afe3bb1 --- /dev/null +++ b/profiler_gui/resources.qrc @@ -0,0 +1,8 @@ + + + icons/logo.svg + icons/off.svg + icons/open-folder.svg + icons/reload.svg + + From 86314b89ba8d4694b0bec2f1681c498e2b3a2bc4 Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Wed, 14 Sep 2016 21:53:06 +0300 Subject: [PATCH 16/25] (profiler_core) Windows event tracing: if we can't get process name at the moment, write process id at least --- src/event_trace_win.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/event_trace_win.cpp b/src/event_trace_win.cpp index 21f4ba0..fadce8d 100644 --- a/src/event_trace_win.cpp +++ b/src/event_trace_win.cpp @@ -94,14 +94,17 @@ namespace profiler { static TCHAR buf[MAX_PATH] = {}; // Using static is safe because processTraceEvent() is called from one thread auto success = GetModuleBaseName(hProc, 0, buf, MAX_PATH); - if (success) + if (pinfo->name.empty()) { static char numbuf[128] = {}; sprintf(numbuf, "%u ", pid); - pinfo->name = numbuf; - pinfo->name += buf; pinfo->id = pid; + } + + if (success) + { + pinfo->name += buf; pinfo->valid = 1; //printf("PROCESS %u is %s\n", pid, buf); } From 89c2dc82c7c49fef4c70645a562f5b2b3f89bdc9 Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Wed, 14 Sep 2016 23:12:41 +0300 Subject: [PATCH 17/25] (profiler_gui) More icons --- profiler_gui/icons/attribution.txt | 17 +++++-- profiler_gui/icons/collapse.svg | 41 ++++++++++++++++ profiler_gui/icons/colors-black.svg | 59 +++++++++++++++++++++++ profiler_gui/icons/colors.svg | 73 +++++++++++++++++++++++++++++ profiler_gui/icons/expand.svg | 41 ++++++++++++++++ profiler_gui/icons/lan.svg | 58 +++++++++++++++++++++++ profiler_gui/icons/save.svg | 43 +++++++++++++++++ profiler_gui/icons/statistics.svg | 47 +++++++++++++++++++ profiler_gui/icons/statistics2.svg | 48 +++++++++++++++++++ profiler_gui/icons/wifi.svg | 43 +++++++++++++++++ profiler_gui/resources.qrc | 9 ++++ 11 files changed, 475 insertions(+), 4 deletions(-) create mode 100644 profiler_gui/icons/collapse.svg create mode 100644 profiler_gui/icons/colors-black.svg create mode 100644 profiler_gui/icons/colors.svg create mode 100644 profiler_gui/icons/expand.svg create mode 100644 profiler_gui/icons/lan.svg create mode 100644 profiler_gui/icons/save.svg create mode 100644 profiler_gui/icons/statistics.svg create mode 100644 profiler_gui/icons/statistics2.svg create mode 100644 profiler_gui/icons/wifi.svg diff --git a/profiler_gui/icons/attribution.txt b/profiler_gui/icons/attribution.txt index 0c406e6..ef024ce 100644 --- a/profiler_gui/icons/attribution.txt +++ b/profiler_gui/icons/attribution.txt @@ -1,4 +1,13 @@ -logo.svg - Icon made by Freepik from www.flaticon.com -off.svg - Icon made by Freepik from www.flaticon.com -open-folder.svg - Icon made by Freepik from www.flaticon.com -reload.svg - Icon made by Freepik from www.flaticon.com \ No newline at end of file +logo.svg - Icon made by Freepik from www.flaticon.com +off.svg - Icon made by Freepik from www.flaticon.com +open-folder.svg - Icon made by Freepik from www.flaticon.com +reload.svg - Icon made by Freepik from www.flaticon.com +expand.svg - Icon made by Freepik from www.flaticon.com +collapse.svg - Icon made by Freepik from www.flaticon.com +colors.svg - Icon made by Freepik from www.flaticon.com +colors-black.svg - Icon made by Freepik from www.flaticon.com +save.svg - Icon made by Freepik from www.flaticon.com +statistics.svg - Icon made by Freepik from www.flaticon.com +statistics2.svg - Icon made by Freepik from www.flaticon.com +lan.svg - Icon made by Freepik from www.flaticon.com +wifi.svg - Icon made by Freepik from www.flaticon.com \ No newline at end of file diff --git a/profiler_gui/icons/collapse.svg b/profiler_gui/icons/collapse.svg new file mode 100644 index 0000000..090484a --- /dev/null +++ b/profiler_gui/icons/collapse.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/profiler_gui/icons/colors-black.svg b/profiler_gui/icons/colors-black.svg new file mode 100644 index 0000000..90aaac4 --- /dev/null +++ b/profiler_gui/icons/colors-black.svg @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/profiler_gui/icons/colors.svg b/profiler_gui/icons/colors.svg new file mode 100644 index 0000000..5b022ac --- /dev/null +++ b/profiler_gui/icons/colors.svg @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/profiler_gui/icons/expand.svg b/profiler_gui/icons/expand.svg new file mode 100644 index 0000000..c66e1e7 --- /dev/null +++ b/profiler_gui/icons/expand.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/profiler_gui/icons/lan.svg b/profiler_gui/icons/lan.svg new file mode 100644 index 0000000..e7df402 --- /dev/null +++ b/profiler_gui/icons/lan.svg @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/profiler_gui/icons/save.svg b/profiler_gui/icons/save.svg new file mode 100644 index 0000000..a3cbb0f --- /dev/null +++ b/profiler_gui/icons/save.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/profiler_gui/icons/statistics.svg b/profiler_gui/icons/statistics.svg new file mode 100644 index 0000000..bec725a --- /dev/null +++ b/profiler_gui/icons/statistics.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/profiler_gui/icons/statistics2.svg b/profiler_gui/icons/statistics2.svg new file mode 100644 index 0000000..639ad8c --- /dev/null +++ b/profiler_gui/icons/statistics2.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/profiler_gui/icons/wifi.svg b/profiler_gui/icons/wifi.svg new file mode 100644 index 0000000..014ff0a --- /dev/null +++ b/profiler_gui/icons/wifi.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/profiler_gui/resources.qrc b/profiler_gui/resources.qrc index afe3bb1..c4e96c3 100644 --- a/profiler_gui/resources.qrc +++ b/profiler_gui/resources.qrc @@ -4,5 +4,14 @@ icons/off.svg icons/open-folder.svg icons/reload.svg + icons/expand.svg + icons/collapse.svg + icons/colors.svg + icons/colors-black.svg + icons/save.svg + icons/statistics.svg + icons/statistics2.svg + icons/lan.svg + icons/wifi.svg From 0166038e7446d46fa7d462e42d64c0a17e7f6793 Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Wed, 14 Sep 2016 23:13:38 +0300 Subject: [PATCH 18/25] (profiler_gui) Enable/Disable statistics --- profiler_gui/globals.cpp | 1 + profiler_gui/globals.h | 1 + profiler_gui/main_window.cpp | 66 ++++++++++++++++++++++++++++++++++-- profiler_gui/main_window.h | 1 + 4 files changed, 66 insertions(+), 3 deletions(-) diff --git a/profiler_gui/globals.cpp b/profiler_gui/globals.cpp index a5021e1..7397e7c 100644 --- a/profiler_gui/globals.cpp +++ b/profiler_gui/globals.cpp @@ -47,6 +47,7 @@ namespace profiler_gui { : selected_thread(0) , selected_block(::profiler_gui::numeric_max()) , chrono_text_position(ChronoTextPosition_Center) + , enable_statistics(true) , draw_graphics_items_borders(true) , display_only_relevant_stats(true) , collapse_items_on_tree_close(false) diff --git a/profiler_gui/globals.h b/profiler_gui/globals.h index 61cbc47..f4bc04f 100644 --- a/profiler_gui/globals.h +++ b/profiler_gui/globals.h @@ -84,6 +84,7 @@ namespace profiler_gui { ::profiler::thread_id_t selected_thread; ///< Current selected thread id unsigned int selected_block; ///< Current selected profiler block index ChronometerTextPosition chrono_text_position; ///< + bool enable_statistics; ///< Enable gathering and using statistics (Disable if you want to consume less memory) bool draw_graphics_items_borders; ///< Draw borders for graphics blocks or not bool display_only_relevant_stats; ///< Display only relevant information in ProfTreeWidget (excludes min, max, average times if there are only 1 calls number) bool collapse_items_on_tree_close; ///< Collapse all items which were displayed in the hierarchy tree after tree close/reset diff --git a/profiler_gui/main_window.cpp b/profiler_gui/main_window.cpp index e231ddc..91d729a 100644 --- a/profiler_gui/main_window.cpp +++ b/profiler_gui/main_window.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -111,9 +112,11 @@ EasyMainWindow::EasyMainWindow() : Parent(), m_treeWidget(nullptr), m_graphicsVi action = menu->addAction("Expand all"); connect(action, &QAction::triggered, this, &This::onExpandAllClicked); + { QIcon icon(":/Expand"); if (!icon.isNull()) action->setIcon(icon); } action = menu->addAction("Collapse all"); connect(action, &QAction::triggered, this, &This::onCollapseAllClicked); + { QIcon icon(":/Collapse"); if (!icon.isNull()) action->setIcon(icon); } menu->addSeparator(); action = menu->addAction("Draw items' borders"); @@ -170,6 +173,28 @@ EasyMainWindow::EasyMainWindow() : Parent(), m_treeWidget(nullptr), m_graphicsVi menu = new QMenu("&Settings"); + action = new QAction("Statistics enabled", nullptr); + action->setCheckable(true); + action->setChecked(EASY_GLOBALS.enable_statistics); + connect(action, &QAction::triggered, this, &This::onEnableDisableStatistics); + if (EASY_GLOBALS.enable_statistics) + { + auto f = action->font(); + f.setBold(true); + action->setFont(f); + QIcon icon(":/Stats"); + if (!icon.isNull()) + action->setIcon(icon); + } + else + { + action->setText("Statistics disabled"); + QIcon icon(":/Stats-off"); + if (!icon.isNull()) + action->setIcon(icon); + } + menu->addAction(action); + submenu = menu->addMenu("&Encoding"); actionGroup = new QActionGroup(this); actionGroup->setExclusive(true); @@ -272,6 +297,34 @@ void EasyMainWindow::onChronoTextPosChanged(bool) emit EASY_GLOBALS.events.chronoPositionChanged(); } +void EasyMainWindow::onEnableDisableStatistics(bool _checked) +{ + EASY_GLOBALS.enable_statistics = _checked; + + auto action = qobject_cast(sender()); + if (action != nullptr) + { + auto f = action->font(); + f.setBold(_checked); + action->setFont(f); + + if (_checked) + { + action->setText("Statistics enabled"); + QIcon icon(":/Stats"); + if (!icon.isNull()) + action->setIcon(icon); + } + else + { + action->setText("Statistics disabled"); + QIcon icon(":/Stats-off"); + if (!icon.isNull()) + action->setIcon(icon); + } + } +} + void EasyMainWindow::onDrawBordersChanged(bool _checked) { EASY_GLOBALS.draw_graphics_items_borders = _checked; @@ -372,6 +425,12 @@ void EasyMainWindow::loadSettings() EASY_GLOBALS.bind_scene_and_tree_expand_status = flag.toBool(); } + flag = settings.value("enable_statistics"); + if (!flag.isNull()) + { + EASY_GLOBALS.enable_statistics = flag.toBool(); + } + QString encoding = settings.value("encoding", "UTF-8").toString(); auto default_codec_mib = QTextCodec::codecForName(encoding.toStdString().c_str())->mibEnum(); auto default_codec = QTextCodec::codecForMib(default_codec_mib); @@ -402,6 +461,7 @@ void EasyMainWindow::saveSettingsAndGeometry() settings.setValue("collapse_items_on_tree_close", EASY_GLOBALS.collapse_items_on_tree_close); settings.setValue("all_items_expanded_by_default", EASY_GLOBALS.all_items_expanded_by_default); settings.setValue("bind_scene_and_tree_expand_status", EASY_GLOBALS.bind_scene_and_tree_expand_status); + settings.setValue("enable_statistics", EASY_GLOBALS.enable_statistics); settings.setValue("encoding", QTextCodec::codecForLocale()->name()); settings.endGroup(); @@ -517,11 +577,11 @@ void EasyFileReader::load(const QString& _filename) interrupt(); m_filename = _filename; - m_thread = ::std::move(::std::thread([this]() { - m_size.store(fillTreesFromFile(m_progress, m_filename.toStdString().c_str(), m_serializedBlocks, m_serializedDescriptors, m_descriptors, m_blocks, m_blocksTree, true)); + m_thread = ::std::move(::std::thread([this](bool _enableStatistics) { + m_size.store(fillTreesFromFile(m_progress, m_filename.toStdString().c_str(), m_serializedBlocks, m_serializedDescriptors, m_descriptors, m_blocks, m_blocksTree, _enableStatistics)); m_progress.store(100); m_bDone.store(true); - })); + }, EASY_GLOBALS.enable_statistics)); } void EasyFileReader::interrupt() diff --git a/profiler_gui/main_window.h b/profiler_gui/main_window.h index 2be7882..6ad19bf 100644 --- a/profiler_gui/main_window.h +++ b/profiler_gui/main_window.h @@ -110,6 +110,7 @@ protected slots: void onExitClicked(bool); void onEncodingChanged(bool); void onChronoTextPosChanged(bool); + void onEnableDisableStatistics(bool); void onDrawBordersChanged(bool); void onCollapseItemsAfterCloseChanged(bool); void onAllItemsExpandedByDefaultChange(bool); From 9748d7274fee07b6a5f38696e6b86205cfc5aa02 Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Wed, 14 Sep 2016 23:14:24 +0300 Subject: [PATCH 19/25] (profiler_gui) Bold and colorized font in EasyTreeWidget header --- profiler_gui/blocks_graphics_view.cpp | 18 ++--- profiler_gui/blocks_tree_widget.cpp | 99 +++++++++++++++++++-------- profiler_gui/common_types.h | 18 +++++ 3 files changed, 92 insertions(+), 43 deletions(-) diff --git a/profiler_gui/blocks_graphics_view.cpp b/profiler_gui/blocks_graphics_view.cpp index acd6f7a..c3420d8 100644 --- a/profiler_gui/blocks_graphics_view.cpp +++ b/profiler_gui/blocks_graphics_view.cpp @@ -102,20 +102,10 @@ const uint64_t IDLE_TIME = 400; const int FLICKER_INTERVAL = 10; // 100Hz const qreal FLICKER_FACTOR = 16.0 / FLICKER_INTERVAL; -QFont EFont(const char* _family, int _size, int _weight = -1) -{ - QFont f; - f.setStyleHint(QFont::Helvetica, QFont::PreferMatch); - f.setFamily(_family); - f.setPointSize(_size); - f.setWeight(_weight); - return f; -} - -const auto BG_FONT = EFont("Helvetica", 10, QFont::Bold); -const auto CHRONOMETER_FONT = EFont("Helvetica", 16, QFont::Bold); -const auto ITEMS_FONT = EFont("Helvetica", 10, QFont::Medium); -const auto SELECTED_ITEM_FONT = EFont("Helvetica", 10, QFont::Bold); +const auto BG_FONT = ::profiler_gui::EFont("Helvetica", 10, QFont::Bold); +const auto CHRONOMETER_FONT = ::profiler_gui::EFont("Helvetica", 16, QFont::Bold); +const auto ITEMS_FONT = ::profiler_gui::EFont("Helvetica", 10, QFont::Medium); +const auto SELECTED_ITEM_FONT = ::profiler_gui::EFont("Helvetica", 10, QFont::Bold); #ifdef _WIN32 const qreal FONT_METRICS_FACTOR = 1.05; diff --git a/profiler_gui/blocks_tree_widget.cpp b/profiler_gui/blocks_tree_widget.cpp index 3418eed..48757ee 100644 --- a/profiler_gui/blocks_tree_widget.cpp +++ b/profiler_gui/blocks_tree_widget.cpp @@ -38,6 +38,7 @@ ************************************************************************/ #include +#include #include #include #include @@ -78,44 +79,73 @@ EasyTreeWidget::EasyTreeWidget(QWidget* _parent) setSortingEnabled(false); setColumnCount(COL_COLUMNS_NUMBER); - auto header = new QTreeWidgetItem(); + auto header_item = new QTreeWidgetItem(); + auto f = header()->font(); + f.setBold(true); + header()->setFont(f);// ::profiler_gui::EFont("Helvetica", 9, QFont::Bold)); - header->setText(COL_NAME, "Name"); + header_item->setText(COL_NAME, "Name"); - header->setText(COL_BEGIN, "Begin, ms"); + header_item->setText(COL_BEGIN, "Begin, ms"); - header->setText(COL_DURATION, "Duration"); - header->setText(COL_SELF_DURATION, "Self Dur."); - //header->setToolTip(COL_SELF_DURATION, ""); - header->setText(COL_DURATION_SUM_PER_PARENT, "Tot. Dur./Parent"); - header->setText(COL_DURATION_SUM_PER_FRAME, "Tot. Dur./Frame"); - header->setText(COL_DURATION_SUM_PER_THREAD, "Tot. Dur./Thread"); + header_item->setText(COL_DURATION, "Duration"); + header_item->setText(COL_SELF_DURATION, "Self Dur."); + //header_item->setToolTip(COL_SELF_DURATION, ""); + header_item->setText(COL_DURATION_SUM_PER_PARENT, "Tot. Dur./Parent"); + header_item->setText(COL_DURATION_SUM_PER_FRAME, "Tot. Dur./Frame"); + header_item->setText(COL_DURATION_SUM_PER_THREAD, "Tot. Dur./Thread"); - header->setText(COL_SELF_DURATION_PERCENT, "Self %"); - header->setText(COL_PERCENT_PER_PARENT, "% / Parent"); - header->setText(COL_PERCENT_PER_FRAME, "% / Frame"); - header->setText(COL_PERCENT_SUM_PER_FRAME, "Tot. % / Frame"); - header->setText(COL_PERCENT_SUM_PER_PARENT, "Tot. % / Parent"); - header->setText(COL_PERCENT_SUM_PER_THREAD, "Tot. % / Thread"); + header_item->setText(COL_SELF_DURATION_PERCENT, "Self %"); + header_item->setText(COL_PERCENT_PER_PARENT, "% / Parent"); + header_item->setText(COL_PERCENT_PER_FRAME, "% / Frame"); + header_item->setText(COL_PERCENT_SUM_PER_FRAME, "Tot. % / Frame"); + header_item->setText(COL_PERCENT_SUM_PER_PARENT, "Tot. % / Parent"); + header_item->setText(COL_PERCENT_SUM_PER_THREAD, "Tot. % / Thread"); - header->setText(COL_END, "End, ms"); + header_item->setText(COL_END, "End, ms"); - header->setText(COL_MIN_PER_FRAME, "Min dur./Frame"); - header->setText(COL_MAX_PER_FRAME, "Max dur./Frame"); - header->setText(COL_AVERAGE_PER_FRAME, "Average dur./Frame"); - header->setText(COL_NCALLS_PER_FRAME, "N Calls/Frame"); + header_item->setText(COL_MIN_PER_FRAME, "Min dur./Frame"); + header_item->setText(COL_MAX_PER_FRAME, "Max dur./Frame"); + header_item->setText(COL_AVERAGE_PER_FRAME, "Average dur./Frame"); + header_item->setText(COL_NCALLS_PER_FRAME, "N Calls/Frame"); - header->setText(COL_MIN_PER_PARENT, "Min dur./Parent"); - header->setText(COL_MAX_PER_PARENT, "Max dur./Parent"); - header->setText(COL_AVERAGE_PER_PARENT, "Average dur./Parent"); - header->setText(COL_NCALLS_PER_PARENT, "N Calls/Parent"); + header_item->setText(COL_MIN_PER_PARENT, "Min dur./Parent"); + header_item->setText(COL_MAX_PER_PARENT, "Max dur./Parent"); + header_item->setText(COL_AVERAGE_PER_PARENT, "Average dur./Parent"); + header_item->setText(COL_NCALLS_PER_PARENT, "N Calls/Parent"); - header->setText(COL_MIN_PER_THREAD, "Min dur./Thread"); - header->setText(COL_MAX_PER_THREAD, "Max dur./Thread"); - header->setText(COL_AVERAGE_PER_THREAD, "Average dur./Thread"); - header->setText(COL_NCALLS_PER_THREAD, "N Calls/Thread"); + header_item->setText(COL_MIN_PER_THREAD, "Min dur./Thread"); + header_item->setText(COL_MAX_PER_THREAD, "Max dur./Thread"); + header_item->setText(COL_AVERAGE_PER_THREAD, "Average dur./Thread"); + header_item->setText(COL_NCALLS_PER_THREAD, "N Calls/Thread"); - setHeaderItem(header); + auto color = QColor::fromRgb(::profiler::colors::DeepOrange900); + header_item->setForeground(COL_MIN_PER_THREAD, color); + header_item->setForeground(COL_MAX_PER_THREAD, color); + header_item->setForeground(COL_AVERAGE_PER_THREAD, color); + header_item->setForeground(COL_NCALLS_PER_THREAD, color); + header_item->setForeground(COL_PERCENT_SUM_PER_THREAD, color); + header_item->setForeground(COL_DURATION_SUM_PER_THREAD, color); + + color = QColor::fromRgb(::profiler::colors::Blue900); + header_item->setForeground(COL_MIN_PER_FRAME, color); + header_item->setForeground(COL_MAX_PER_FRAME, color); + header_item->setForeground(COL_AVERAGE_PER_FRAME, color); + header_item->setForeground(COL_NCALLS_PER_FRAME, color); + header_item->setForeground(COL_PERCENT_SUM_PER_FRAME, color); + header_item->setForeground(COL_DURATION_SUM_PER_FRAME, color); + header_item->setForeground(COL_PERCENT_PER_FRAME, color); + + color = QColor::fromRgb(::profiler::colors::Teal900); + header_item->setForeground(COL_MIN_PER_PARENT, color); + header_item->setForeground(COL_MAX_PER_PARENT, color); + header_item->setForeground(COL_AVERAGE_PER_PARENT, color); + header_item->setForeground(COL_NCALLS_PER_PARENT, color); + header_item->setForeground(COL_PERCENT_SUM_PER_PARENT, color); + header_item->setForeground(COL_DURATION_SUM_PER_PARENT, color); + header_item->setForeground(COL_PERCENT_PER_PARENT, color); + + setHeaderItem(header_item); connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedThreadChanged, this, &This::onSelectedThreadChange); connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedBlockChanged, this, &This::onSelectedBlockChange); @@ -329,9 +359,11 @@ void EasyTreeWidget::contextMenuEvent(QContextMenuEvent* _event) { action = menu.addAction("Expand all"); connect(action, &QAction::triggered, this, &This::onExpandAllClicked); + { QIcon icon(":/Expand"); if (!icon.isNull()) action->setIcon(icon); } action = menu.addAction("Collapse all"); connect(action, &QAction::triggered, this, &This::onCollapseAllClicked); + { QIcon icon(":/Collapse"); if (!icon.isNull()) action->setIcon(icon); } if (item != nullptr && col >= 0) { @@ -339,9 +371,11 @@ void EasyTreeWidget::contextMenuEvent(QContextMenuEvent* _event) action = menu.addAction("Expand all children"); connect(action, &QAction::triggered, this, &This::onExpandAllChildrenClicked); + { QIcon icon(":/Expand"); if (!icon.isNull()) action->setIcon(icon); } action = menu.addAction("Collapse all children"); connect(action, &QAction::triggered, this, &This::onCollapseAllChildrenClicked); + { QIcon icon(":/Collapse"); if (!icon.isNull()) action->setIcon(icon); } } menu.addSeparator(); @@ -351,6 +385,13 @@ void EasyTreeWidget::contextMenuEvent(QContextMenuEvent* _event) action->setCheckable(true); action->setChecked(m_bColorRows); connect(action, &QAction::triggered, this, &This::onColorizeRowsTriggered); + if (m_bColorRows) { + auto f = action->font(); + f.setBold(true); + action->setFont(f); + { QIcon icon(":/Color"); if (!icon.isNull()) action->setIcon(icon); } + } + else { QIcon icon(":/NoColor"); if (!icon.isNull()) action->setIcon(icon); } if (item != nullptr && item->parent() != nullptr) { diff --git a/profiler_gui/common_types.h b/profiler_gui/common_types.h index 4c2e3c4..8c9e63e 100644 --- a/profiler_gui/common_types.h +++ b/profiler_gui/common_types.h @@ -37,6 +37,7 @@ #include #include #include +#include #include "profiler/reader.h" ////////////////////////////////////////////////////////////////////////// @@ -281,6 +282,23 @@ inline int percent(::profiler::timestamp_t _partial, ::profiler::timestamp_t _to ////////////////////////////////////////////////////////////////////////// +inline QFont EFont(QFont::StyleHint _hint, const char* _family, int _size, int _weight = -1) +{ + QFont f; + f.setStyleHint(_hint, QFont::PreferMatch); + f.setFamily(_family); + f.setPointSize(_size); + f.setWeight(_weight); + return f; +} + +inline QFont EFont(const char* _family, int _size, int _weight = -1) +{ + return EFont(QFont::Helvetica, _family, _size, _weight); +} + +////////////////////////////////////////////////////////////////////////// + } // END of namespace profiler_gui. ////////////////////////////////////////////////////////////////////////// From 330951cb62c95725987cfe6332a2a5fdaf9629a0 Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Wed, 14 Sep 2016 23:14:47 +0300 Subject: [PATCH 20/25] (profiler_gui) Low priority for deleter thread --- profiler_gui/tree_widget_loader.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/profiler_gui/tree_widget_loader.cpp b/profiler_gui/tree_widget_loader.cpp index b93ee68..08af697 100644 --- a/profiler_gui/tree_widget_loader.cpp +++ b/profiler_gui/tree_widget_loader.cpp @@ -34,6 +34,10 @@ #include "tree_widget_item.h" #include "globals.h" +#ifdef _WIN32 +#include +#endif + #ifdef max #undef max #endif @@ -111,6 +115,11 @@ void EasyTreeWidgetLoader::interrupt() for (auto item : _items) delete item.second; }, ::std::move(m_topLevelItems)); + +#ifdef _WIN32 + SetThreadPriority(deleter_thread.native_handle(), THREAD_PRIORITY_LOWEST); +#endif + deleter_thread.detach(); m_items.clear(); From 795d347d2a1e99b48c871e1031bfcd9b27a03d9f Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Wed, 14 Sep 2016 23:23:09 +0300 Subject: [PATCH 21/25] Linux build --- src/hashed_cstr.h | 12 ++++++++++++ src/profile_manager.cpp | 4 ++++ src/profile_manager.h | 5 +++++ 3 files changed, 21 insertions(+) diff --git a/src/hashed_cstr.h b/src/hashed_cstr.h index b9896d9..ca775fb 100644 --- a/src/hashed_cstr.h +++ b/src/hashed_cstr.h @@ -203,6 +203,18 @@ namespace profiler { { } + hashed_stdstring(const char* _str, size_t _hash_code) : m_str(_str), m_hash(_hash_code) + { + } + + hashed_stdstring(const ::std::string& _str, size_t _hash_code) : m_str(_str), m_hash(_hash_code) + { + } + + hashed_stdstring(::std::string&& _str, size_t _hash_code) : m_str(::std::forward<::std::string&&>(_str)), m_hash(_hash_code) + { + } + hashed_stdstring(const hashed_stdstring&) = default; hashed_stdstring& operator = (const hashed_stdstring&) = default; diff --git a/src/profile_manager.cpp b/src/profile_manager.cpp index 1c65f2d..c91cc30 100644 --- a/src/profile_manager.cpp +++ b/src/profile_manager.cpp @@ -490,8 +490,12 @@ void ProfileManager::setBlockEnabled(profiler::block_id_t _id, const profiler::h } else { +#ifdef _WIN32 blocks_enable_status_t::key_type key(_key.c_str(), _key.size(), _key.hcode()); m_blocksEnableStatus[key] = _enabled; +#else + m_blocksEnableStatus[_key] = _enabled; +#endif } } diff --git a/src/profile_manager.h b/src/profile_manager.h index 0116763..4e013f7 100644 --- a/src/profile_manager.h +++ b/src/profile_manager.h @@ -312,7 +312,12 @@ class ProfileManager final typedef profiler::guard_lock guard_lock_t; typedef std::map map_of_threads_stacks; typedef std::vector block_descriptors_t; + +#ifdef _WIN32 typedef std::unordered_map blocks_enable_status_t; +#else + typedef std::unordered_map blocks_enable_status_t; +#endif map_of_threads_stacks m_threads; block_descriptors_t m_descriptors; From f0ee9215bd2835b65b6503861795e4bea4258203 Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Wed, 14 Sep 2016 23:34:56 +0300 Subject: [PATCH 22/25] Warnings fix --- include/profiler/profiler.h | 3 ++- profiler_gui/blocks_graphics_view.cpp | 1 - src/profile_manager.cpp | 6 +++--- src/profile_manager.h | 4 ++-- src/reader.cpp | 1 - 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/include/profiler/profiler.h b/include/profiler/profiler.h index c43870c..7ab3b99 100644 --- a/include/profiler/profiler.h +++ b/include/profiler/profiler.h @@ -383,6 +383,7 @@ namespace profiler { public: explicit BlockDescRef(const BaseBlockDescriptor& _desc) : m_desc(_desc) { } + explicit BlockDescRef(const BaseBlockDescriptor* _desc) : m_desc(*_desc) { } inline operator const BaseBlockDescriptor& () const { return m_desc; } ~BlockDescRef(); @@ -407,7 +408,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(bool _enabled, const char* _autogenUniqueId, const char* _compiletimeName, const char* _filename, int _line, block_type_t _block_type, color_t _color); /** Stores event in the blocks list. diff --git a/profiler_gui/blocks_graphics_view.cpp b/profiler_gui/blocks_graphics_view.cpp index c3420d8..317ec51 100644 --- a/profiler_gui/blocks_graphics_view.cpp +++ b/profiler_gui/blocks_graphics_view.cpp @@ -816,7 +816,6 @@ const ::profiler_gui::EasyBlock* EasyGraphicsItem::intersectEvent(const QPointF& } const auto sceneView = view(); - const auto currentScale = view()->scale(); auto firstSync = ::std::lower_bound(m_pRoot->sync.begin(), m_pRoot->sync.end(), _pos.x(), [&sceneView](::profiler::block_index_t _index, qreal _value) { return sceneView->time2position(blocksTree(_index).node->begin()) < _value; diff --git a/src/profile_manager.cpp b/src/profile_manager.cpp index c91cc30..7b734fd 100644 --- a/src/profile_manager.cpp +++ b/src/profile_manager.cpp @@ -47,7 +47,7 @@ extern timestamp_t getCurrentTime(); 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(bool _enabled, const char* _autogenUniqueId, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color) { return MANAGER.addBlockDescriptor(_enabled, _autogenUniqueId, _name, _filename, _line, _block_type, _color); } @@ -153,8 +153,8 @@ void ThreadStorage::storeBlock(const profiler::Block& block) auto size = static_cast(sizeof(BaseBlockData) + name_length + 1); #if EASY_MEASURE_STORAGE_EXPAND != 0 - const bool expanded = desc.enabled() && blocks.closedList.need_expand(size); - profiler::Block b(0ULL, desc.id(), ""); + const bool expanded = desc->enabled() && blocks.closedList.need_expand(size); + profiler::Block b(0ULL, desc->id(), ""); if (expanded) b.start(); #endif diff --git a/src/profile_manager.h b/src/profile_manager.h index 4e013f7..c4d2a49 100644 --- a/src/profile_manager.h +++ b/src/profile_manager.h @@ -342,7 +342,7 @@ public: ~ProfileManager(); template - const profiler::BaseBlockDescriptor& addBlockDescriptor(bool _enabledByDefault, const char* _autogenUniqueId, TArgs ... _args) + const profiler::BaseBlockDescriptor* addBlockDescriptor(bool _enabledByDefault, const char* _autogenUniqueId, TArgs ... _args) { auto desc = new profiler::BlockDescriptor(m_usedMemorySize, _enabledByDefault, _args...); @@ -362,7 +362,7 @@ public: desc->m_pEnable = &m_blocksEnableStatus.emplace(key, desc->enabled()).first->second; } - return *desc; + return desc; } void storeBlock(const profiler::BaseBlockDescriptor& _desc, const char* _runtimeName); diff --git a/src/reader.cpp b/src/reader.cpp index 0830c6b..7740c12 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -342,7 +342,6 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic& progr auto& per_parent_statistics = parent_statistics[thread_id]; auto& per_thread_statistics = thread_statistics[thread_id]; - auto descriptor = descriptors[baseData->id()]; if (*tree.node->name() != 0) { From c56acf50ed134e9cb6f8ddcbb9977c40f3514490 Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Thu, 15 Sep 2016 00:08:42 +0300 Subject: [PATCH 23/25] Warning fix --- profiler_gui/graphics_scrollbar.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/profiler_gui/graphics_scrollbar.cpp b/profiler_gui/graphics_scrollbar.cpp index 767b2f9..782c1fc 100644 --- a/profiler_gui/graphics_scrollbar.cpp +++ b/profiler_gui/graphics_scrollbar.cpp @@ -40,8 +40,6 @@ ////////////////////////////////////////////////////////////////////////// -const qreal SCALING_COEFFICIENT = 1.25; -//const qreal SCALING_COEFFICIENT_INV = 1.0 / SCALING_COEFFICIENT; const int DEFAULT_TOP = -40; const int DEFAULT_HEIGHT = 80; const int INDICATOR_SIZE = 8; From fd856478e5ea6d8f99e4720dd1acf1636d7817b1 Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Thu, 15 Sep 2016 00:08:53 +0300 Subject: [PATCH 24/25] Update README.md --- README.md | 66 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index ce40bdd..0777b42 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,10 @@ # About Lightweight profiler library for c++ -You can profile any function in you code. Furthermore this library provide measuring time of any block of code. Also the library can capture system's context switch. -You can see the results of measuring in simple gui application which provide full statistic and render beautiful timeline. +You can profile any function in you code. Furthermore this library provide measuring time of any block of code. +Also the library can capture system's context switch events between threads. Captured information includes duration, +target thread id, thread owner process id, thread owner process name. +You can see the results of measuring in simple GUI application which provides full statistics and renders beautiful time-line. # Build @@ -24,7 +26,7 @@ For core: For GUI: * Qt 5.3.0 or later -## linux +## Linux ```bash $ mkdir build @@ -33,50 +35,78 @@ $ cmake .. $ make ``` -## windows +## Windows -If you use qtcreator IDE you can just open `CMakeLists.txt` file in root directory. +If you are using QtCreator IDE you can just open `CMakeLists.txt` file in root directory. +If you are using Visual Studio you can generate solution by cmake generator command. -If you use Visual Studio you can generate solution by cmake command. In this case you should specify path to cmake scripts in Qt5 dir (usually in lib/cmake subdir), for example: +### Way 1 +Specify path to cmake scripts in Qt5 dir (usually in lib/cmake subdir), for example: ```batch $ mkdir build $ cd build -$ cmake -DCMAKE_PREFIX_PATH="C:\Qt\5.3\msvc2013_64\lib\cmake" .. +$ cmake -DCMAKE_PREFIX_PATH="C:\Qt\5.3\msvc2013_64\lib\cmake" .. -G "Visual Studio 12 2013 Win64" +``` + +### Way 2 +Create system variable "Qt5Widgets_DIR" and set it's value to "[path-to-Qt5-binaries]\lib\cmake\Qt5Widgets". +For example, "C:\Qt\5.3\msvc2013_64\lib\cmake\Qt5Widgets". +And then run cmake generator as follows: +```batch +$ mkdir build +$ cd build +$ cmake .. -G "Visual Studio 12 2013 Win64" ``` # Usage -First of all you can specify path to include directory which contains `include/profiler` directory. For linking with ease_profiler you can specify path to library. +First of all you can specify path to include directory which contains `include/profiler` directory. +For linking with easy_profiler you can specify path to library. Example of usage. -This code snippet will generate block with function name and grouped it in Magenta group: +This code snippet will generate block with function name and Magenta color: ```cpp #include -void frame(){ +void frame() { EASY_FUNCTION(profiler::colors::Magenta); prepareRender(); calculatePhysics(); } ``` -To profile any block you may do this as following. You can specify these blocks also with Google material design color or just set name of block (in this case color will be OrangeA100): + +To profile any block you may do this as following. +You can specify these blocks also with Google material design colors or just set name of the block +(in this case it will have default color which is Amber100): ```cpp #include -void frame(){ - //some code - EASY_BLOCK("Calculating summ"); - for(int i = 0; i < 10; i++){ +void frame() { + // some code + EASY_BLOCK("Calculating sum"); + int sum = 0; + for (int i = 0; i < 10; ++i) sum += i; - } EASY_END_BLOCK; EASY_BLOCK("Calculating multiplication", profiler::colors::Blue50); - for(int i = 0; i < 10; i++){ + int mul = 1; + for (int i = 1; i < 11; ++i) mul *= i; - } EASY_END_BLOCK; } ``` + +You can also use your color set. EasyProfiler is using standard 32-bit ARGB color format. +Example: +```cpp +#include + +void foo() { + EASY_FUNCTION(0xfff080aa); + // some code +} +``` + [![Analytics](https://ga-beacon.appspot.com/UA-82899176-1/easy_profiler/readme)](https://github.com/yse/easy_profiler) From 32d446f2c46c0c443903e9cdb2046ae2037d3c87 Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Thu, 15 Sep 2016 00:33:47 +0300 Subject: [PATCH 25/25] Update README.md --- README.md | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 0777b42..a8b07cb 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,18 @@ # About -Lightweight profiler library for c++ +Lightweight profiler library for c++ You can profile any function in you code. Furthermore this library provide measuring time of any block of code. -Also the library can capture system's context switch events between threads. Captured information includes duration, -target thread id, thread owner process id, thread owner process name. +For example, information for 12 millions of blocks is using less than 300Mb of memory. +Working profiler slows your application execution for only 1-2%. + +Disabled profiler will not affect your application execution in any way. You can leave it in your Release build +and enable it in run-time at any moment during application launch to see what is happening at the moment. + +Also the library can capture system's context switch events between threads. Context switch information includes +duration, target thread id, thread owner process id, thread owner process name. + You can see the results of measuring in simple GUI application which provides full statistics and renders beautiful time-line. # Build @@ -41,7 +48,8 @@ If you are using QtCreator IDE you can just open `CMakeLists.txt` file in root d If you are using Visual Studio you can generate solution by cmake generator command. ### Way 1 -Specify path to cmake scripts in Qt5 dir (usually in lib/cmake subdir), for example: +Specify path to cmake scripts in Qt5 dir (usually in lib/cmake subdir) and execute cmake generator command, +for example: ```batch $ mkdir build $ cd build @@ -70,7 +78,7 @@ This code snippet will generate block with function name and Magenta color: #include void frame() { - EASY_FUNCTION(profiler::colors::Magenta); + EASY_FUNCTION(profiler::colors::Magenta); // Magenta block with name "frame" prepareRender(); calculatePhysics(); } @@ -78,19 +86,19 @@ void frame() { To profile any block you may do this as following. You can specify these blocks also with Google material design colors or just set name of the block -(in this case it will have default color which is Amber100): +(in this case it will have default color which is `Amber100`): ```cpp #include void frame() { // some code - EASY_BLOCK("Calculating sum"); + EASY_BLOCK("Calculating sum"); // Block with default color int sum = 0; for (int i = 0; i < 10; ++i) sum += i; EASY_END_BLOCK; - EASY_BLOCK("Calculating multiplication", profiler::colors::Blue50); + EASY_BLOCK("Calculating multiplication", profiler::colors::Blue500); // Blue block int mul = 1; for (int i = 1; i < 11; ++i) mul *= i; @@ -98,13 +106,13 @@ void frame() { } ``` -You can also use your color set. EasyProfiler is using standard 32-bit ARGB color format. +You can also use your own colors. easy_profiler is using standard 32-bit ARGB color format. Example: ```cpp #include void foo() { - EASY_FUNCTION(0xfff080aa); + EASY_FUNCTION(0xfff080aa); // Function block with custom color // some code } ```