On Tue, 16 Oct 2018 15:53:05 +0000 Yordan Karadzhov <ykaradzhov@xxxxxxxxxx> wrote: > F > diff --git a/kernel-shark-qt/src/KsTraceGraph.cpp b/kernel-shark-qt/src/KsTraceGraph.cpp > new file mode 100644 > index 0000000..21a09d0 > --- /dev/null > +++ b/kernel-shark-qt/src/KsTraceGraph.cpp > @@ -0,0 +1,690 @@ > +// SPDX-License-Identifier: LGPL-2.1 > + > +/* > + * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <ykaradzhov@xxxxxxxxxx> > + */ > + > +/** > + * @file KsTraceGraph.cpp > + * @brief KernelShark Trace Graph widget. > + */ > + > +// KernelShark > +#include "KsUtils.hpp" > +#include "KsDualMarker.hpp" > +#include "KsTraceGraph.hpp" > + > +/** Create a default (empty) Trace graph widget. */ > +KsTraceGraph::KsTraceGraph(QWidget *parent) > +: QWidget(parent), > + _pointerBar(this), > + _navigationBar(this), > + _zoomInButton("+", this), > + _quickZoomInButton("++", this), > + _zoomOutButton("-", this), > + _quickZoomOutButton("- -", this), > + _scrollLeftButton("<", this), > + _scrollRightButton(">", this), > + _labelP1("Pointer: ", this), > + _labelP2("", this), > + _labelI1("", this), > + _labelI2("", this), > + _labelI3("", this), > + _labelI4("", this), > + _labelI5("", this), > + _scrollArea(this), > + _drawWindow(&_scrollArea), > + _legendWindow(&_drawWindow), > + _legendAxisX(&_drawWindow), > + _labelXMin("", &_legendAxisX), > + _labelXMid("", &_legendAxisX), > + _labelXMax("", &_legendAxisX), > + _glWindow(&_drawWindow), > + _mState(nullptr), > + _data(nullptr), > + _keyPressed(false) > +{ > + auto lamMakeNavButton = [&](QPushButton *b) { > + b->setMaximumWidth(FONT_WIDTH * 5); > + > + connect(b, &QPushButton::released, > + this, &KsTraceGraph::_stopUpdating); > + _navigationBar.addWidget(b); > + }; > + > + _pointerBar.setMaximumHeight(FONT_HEIGHT * 1.75); Why the 1.75? > + _pointerBar.setOrientation(Qt::Horizontal); > + > + _navigationBar.setMaximumHeight(FONT_HEIGHT * 1.75); > + _navigationBar.setMinimumWidth(FONT_WIDTH * 90); Why the 90? > + _navigationBar.setOrientation(Qt::Horizontal); > + > + _pointerBar.addWidget(&_labelP1); > + _labelP2.setFrameStyle(QFrame::Panel | QFrame::Sunken); > + _labelP2.setStyleSheet("QLabel { background-color : white;}"); > + _labelP2.setTextInteractionFlags(Qt::TextSelectableByMouse); > + _labelP2.setFixedWidth(FONT_WIDTH * 16); Why 16? > + _pointerBar.addWidget(&_labelP2); > + _pointerBar.addSeparator(); > + > + _labelI1.setStyleSheet("QLabel {color : blue;}"); > + _labelI2.setStyleSheet("QLabel {color : green;}"); > + _labelI3.setStyleSheet("QLabel {color : red;}"); > + _labelI4.setStyleSheet("QLabel {color : blue;}"); > + _labelI5.setStyleSheet("QLabel {color : green;}"); > + > + _pointerBar.addWidget(&_labelI1); > + _pointerBar.addSeparator(); > + _pointerBar.addWidget(&_labelI2); > + _pointerBar.addSeparator(); > + _pointerBar.addWidget(&_labelI3); > + _pointerBar.addSeparator(); > + _pointerBar.addWidget(&_labelI4); > + _pointerBar.addSeparator(); > + _pointerBar.addWidget(&_labelI5); > + > + _legendAxisX.setFixedHeight(FONT_HEIGHT * 1.5); 1.5? > + _legendAxisX.setLayout(new QHBoxLayout); > + _legendAxisX.layout()->setSpacing(0); > + _legendAxisX.layout()->setContentsMargins(0, 0, FONT_WIDTH, 0); > + > + _labelXMin.setAlignment(Qt::AlignLeft); > + _labelXMid.setAlignment(Qt::AlignHCenter); > + _labelXMax.setAlignment(Qt::AlignRight); > + > + _legendAxisX.layout()->addWidget(&_labelXMin); > + _legendAxisX.layout()->addWidget(&_labelXMid); > + _legendAxisX.layout()->addWidget(&_labelXMax); > + _drawWindow.setMinimumSize(100, 100); The 100s should probably be MACROS > + _drawWindow.setStyleSheet("QWidget {background-color : white;}"); > + > + _drawLayout.setContentsMargins(0, 0, 0, 0); > + _drawLayout.setSpacing(0); > + _drawLayout.addWidget(&_legendAxisX, 0, 1); > + _drawLayout.addWidget(&_legendWindow, 1, 0); > + _drawLayout.addWidget(&_glWindow, 1, 1); > + _drawWindow.setLayout(&_drawLayout); > + > + _drawWindow.installEventFilter(this); > + > + connect(&_glWindow, &KsGLWidget::select, > + this, &KsTraceGraph::markEntry); > + > + connect(&_glWindow, &KsGLWidget::found, > + this, &KsTraceGraph::_setPointerInfo); > + > + connect(&_glWindow, &KsGLWidget::notFound, > + this, &KsTraceGraph::_resetPointer); > + > + connect(&_glWindow, &KsGLWidget::zoomIn, > + this, &KsTraceGraph::_zoomIn); > + > + connect(&_glWindow, &KsGLWidget::zoomOut, > + this, &KsTraceGraph::_zoomOut); > + > + connect(&_glWindow, &KsGLWidget::scrollLeft, > + this, &KsTraceGraph::_scrollLeft); > + > + connect(&_glWindow, &KsGLWidget::scrollRight, > + this, &KsTraceGraph::_scrollRight); > + > + connect(&_glWindow, &KsGLWidget::stopUpdating, > + this, &KsTraceGraph::_stopUpdating); > + > + connect(_glWindow.model(), &KsGraphModel::modelReset, > + this, &KsTraceGraph::_updateTimeLegends); > + > + _scrollArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); > + _scrollArea.setWidget(&_drawWindow); > + > + lamMakeNavButton(&_scrollLeftButton); > + connect(&_scrollLeftButton, &QPushButton::pressed, > + this, &KsTraceGraph::_scrollLeft); > + > + lamMakeNavButton(&_zoomInButton); > + connect(&_zoomInButton, &QPushButton::pressed, > + this, &KsTraceGraph::_zoomIn); > + > + lamMakeNavButton(&_zoomOutButton); > + connect(&_zoomOutButton, &QPushButton::pressed, > + this, &KsTraceGraph::_zoomOut); > + > + lamMakeNavButton(&_scrollRightButton); > + connect(&_scrollRightButton, &QPushButton::pressed, > + this, &KsTraceGraph::_scrollRight); > + > + _navigationBar.addSeparator(); > + > + lamMakeNavButton(&_quickZoomInButton); > + connect(&_quickZoomInButton, &QPushButton::pressed, > + this, &KsTraceGraph::_quickZoomIn); > + > + lamMakeNavButton(&_quickZoomOutButton); > + connect(&_quickZoomOutButton, &QPushButton::pressed, > + this, &KsTraceGraph::_quickZoomOut); > + > + _layout.addWidget(&_pointerBar); > + _layout.addWidget(&_navigationBar); > + _layout.addWidget(&_scrollArea); > + this->setLayout(&_layout); > + updateGeom(); > +} > + > +/** > + * @brief Load and show trace data. > + * > + * @param data: Input location for the KsDataStore object. > + * KsDataStore::loadDataFile() must be called first. > + */ > +void KsTraceGraph::loadData(KsDataStore *data) > +{ > + _data = data; > + _glWindow.loadData(data); > + _updateGraphLegends(); > + updateGeom(); > +} > + > +/** Connect the KsGLWidget widget and the State machine of the Dual marker. */ > +void KsTraceGraph::setMarkerSM(KsDualMarkerSM *m) > +{ > + _mState = m; > + _navigationBar.addSeparator(); > + _mState->placeInToolBar(&_navigationBar); > + _glWindow.setMarkerSM(m); > +} > + > +/** Reset (empty) the widget. */ > +void KsTraceGraph::reset() > +{ > + /* Clear the all graph lists and update. */ > + _glWindow._cpuList = {}; > + _glWindow._taskList = {}; > + > + _labelP2.setText(""); > + for (auto l1: {&_labelI1, &_labelI2, &_labelI3, &_labelI4, &_labelI5}) > + l1->setText(""); > + > + _glWindow.model()->reset(); > + _selfUpdate(); > + for (auto l2: {&_labelXMin, &_labelXMid, &_labelXMax}) > + l2->setText(""); > +} > + > +void KsTraceGraph::_selfUpdate() > +{ > + _updateGraphLegends(); > + _updateTimeLegends(); > + _markerReDraw(); > + updateGeom(); > +} > + > +void KsTraceGraph::_zoomIn() > +{ > + _updateGraphs(GraphActions::ZoomIn); > +} > + > +void KsTraceGraph::_zoomOut() > +{ > + _updateGraphs(GraphActions::ZoomOut); > +} > + > +void KsTraceGraph::_quickZoomIn() > +{ > + /* Bin size will be 100 ns. */ > + _glWindow.model()->quickZoomIn(100); > + if (_mState->activeMarker()._isSet && > + _mState->activeMarker().isVisible()) { > + /* > + * Use the position of the active marker as > + * a focus point of the zoom. > + */ > + uint64_t ts = _mState->activeMarker()._ts; > + _glWindow.model()->jumpTo(ts); > + } > +} > + > +void KsTraceGraph::_quickZoomOut() > +{ > + _glWindow.model()->quickZoomOut(); > +} > + > +void KsTraceGraph::_scrollLeft() > +{ > + _updateGraphs(GraphActions::ScrollLeft); > +} > + > +void KsTraceGraph::_scrollRight() > +{ > + _updateGraphs(GraphActions::ScrollRight); > +} > + > +void KsTraceGraph::_stopUpdating() > +{ > + /* > + * The user is no longer pressing the action button. Reset the > + * "Key Pressed" flag. This will stop the ongoing user action. > + */ > + _keyPressed = false; > +} > + > +void KsTraceGraph::_resetPointer(uint64_t ts, int cpu, int pid) > +{ > + uint64_t sec, usec; > + QString pointer; > + > + kshark_convert_nano(ts, &sec, &usec); > + pointer.sprintf("%lu.%lu", sec, usec); > + _labelP2.setText(pointer); > + > + if (pid > 0 && cpu >= 0) { > + struct kshark_context *kshark_ctx(NULL); > + > + if (!kshark_instance(&kshark_ctx)) > + return; > + > + QString comm(tep_data_comm_from_pid(kshark_ctx->pevent, pid)); > + comm.append("-"); > + comm.append(QString("%1").arg(pid)); > + _labelI1.setText(comm); > + _labelI2.setText(QString("CPU %1").arg(cpu)); > + } else { > + _labelI1.setText(""); > + _labelI2.setText(""); > + } > + > + for (auto const &l: {&_labelI3, &_labelI4, &_labelI5}) { > + l->setText(""); > + } > +} > + > +void KsTraceGraph::_setPointerInfo(size_t i) > +{ > + kshark_entry *e = _data->rows()[i]; > + QString event(kshark_get_event_name_easy(e)); > + QString lat(kshark_get_latency_easy(e)); > + QString info(kshark_get_info_easy(e)); > + QString comm(kshark_get_task_easy(e)); > + QString pointer, elidedText; > + int labelWidth, width; > + uint64_t sec, usec; > + > + kshark_convert_nano(e->ts, &sec, &usec); > + pointer.sprintf("%lu.%lu", sec, usec); > + _labelP2.setText(pointer); > + > + comm.append("-"); > + comm.append(QString("%1").arg(kshark_get_pid_easy(e))); > + > + _labelI1.setText(comm); > + _labelI2.setText(QString("CPU %1").arg(e->cpu)); > + _labelI3.setText(lat); > + _labelI4.setText(event); > + _labelI5.setText(info); > + QCoreApplication::processEvents(); > + > + labelWidth = > + _pointerBar.geometry().right() - _labelI4.geometry().right(); > + if (labelWidth > STRING_WIDTH(info) + FONT_WIDTH * 5) Why 5? > + return; > + > + /* > + * The Info string is too long and cannot be displayed on the toolbar. > + * Try to fit the text in the available space. > + */ > + QFontMetrics metrix(_labelI5.font()); > + width = labelWidth - FONT_WIDTH * 3; Why 3? > + elidedText = metrix.elidedText(info, Qt::ElideRight, width); > + > + while(labelWidth < STRING_WIDTH(elidedText) + FONT_WIDTH * 5) { > + width -= FONT_WIDTH * 3; Why 5 and 3? > + elidedText = metrix.elidedText(info, Qt::ElideRight, width); > + } > + > + _labelI5.setText(elidedText); > + _labelI5.setVisible(true); > + QCoreApplication::processEvents(); > +} > + > +/** > + * @brief Use the active marker to select particular entry. > + * > + * @param row: The index of the entry to be selected by the marker. > + */ > +void KsTraceGraph::markEntry(size_t row) > +{ > + int graph, cpuGrId, taskGrId; > + > + _glWindow.findGraphIds(*_data->rows()[row], &cpuGrId, &taskGrId); > + > + /* > + * If a Task graph has been found, this Task graph will be > + * visible. If no Task graph has been found, make visible > + * the corresponding CPU graph. > + */ > + if (taskGrId >= 0) > + graph = taskGrId; > + else > + graph = cpuGrId; > + > + _scrollArea.ensureVisible(0, > + _legendAxisX.height() + > + _glWindow.vMargin() + > + KS_GRAPH_HEIGHT / 2 + > + graph*(KS_GRAPH_HEIGHT + _glWindow.vSpacing()), > + 50, Why 50? > + KS_GRAPH_HEIGHT / 2 + _glWindow.vSpacing() / 2); > + > + _glWindow.model()->jumpTo(_data->rows()[row]->ts); > + _mState->activeMarker().set(*_data, > + _glWindow.model()->histo(), > + row, cpuGrId, taskGrId); > + > + _mState->updateMarkers(*_data, &_glWindow); > +} > + > +void KsTraceGraph::_markerReDraw() > +{ > + int cpuGrId, taskGrId; > + size_t row; > + > + if (_mState->markerA()._isSet) { > + row = _mState->markerA()._pos; > + _glWindow.findGraphIds(*_data->rows()[row], &cpuGrId, &taskGrId); > + _mState->markerA().set(*_data, > + _glWindow.model()->histo(), > + row, cpuGrId, taskGrId); > + } > + > + if (_mState->markerB()._isSet) { > + row = _mState->markerB()._pos; > + _glWindow.findGraphIds(*_data->rows()[row], &cpuGrId, &taskGrId); > + _mState->markerB().set(*_data, > + _glWindow.model()->histo(), > + row, cpuGrId, taskGrId); > + } > +} > + > +/** > + * @brief Redreaw all CPU graphs. > + * > + * @param v: CPU ids to be plotted. > + */ > +void KsTraceGraph::cpuReDraw(QVector<int> v) > +{ > + _glWindow._cpuList = v; > + _selfUpdate(); > +} > + > +/** > + * @brief Redreaw all Task graphs. > + * > + * @param v: Process ids of the tasks to be plotted. > + */ > +void KsTraceGraph::taskReDraw(QVector<int> v) > +{ > + _glWindow._taskList = v; > + _selfUpdate(); > +} > + > +/** Add (and plot) a CPU graph to the existing list of CPU graphs. */ > +void KsTraceGraph::addCPUPlot(int cpu) > +{ > + if (_glWindow._cpuList.contains(cpu)) > + return; > + > + _glWindow._cpuList.append(cpu); > + _selfUpdate(); > +} > + > +/** Add (and plot) a Task graph to the existing list of Task graphs. */ > +void KsTraceGraph::addTaskPlot(int pid) > +{ > + if (_glWindow._taskList.contains(pid)) > + return; > + > + _glWindow._taskList.append(pid); > + _selfUpdate(); > +} > + > +/** Update the content of all graphs. */ > +void KsTraceGraph::update(KsDataStore *data) > +{ > + _glWindow.model()->update(data); > + _selfUpdate(); > +} > + > +/** Update the geometry of the widget. */ > +void KsTraceGraph::updateGeom() > +{ > + int saWidth, saHeight, dwWidth, hMin; > + > + /* Set the size of the Scroll Area. */ > + saWidth = width() - _layout.contentsMargins().left() - > + _layout.contentsMargins().right(); > + > + saHeight = height() - _pointerBar.height() - > + _navigationBar.height() - > + _layout.spacing() * 2 - > + _layout.contentsMargins().top() - > + _layout.contentsMargins().bottom(); > + > + _scrollArea.resize(saWidth, saHeight); > + > + /* > + * Calculate the width of the Draw Window, taking into account the size > + * of the scroll bar. > + */ > + dwWidth = _scrollArea.width(); > + if (_glWindow.height() + _legendAxisX.height() > _scrollArea.height()) > + dwWidth -= > + qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent); > + > + /* > + * Set the height of the Draw window according to the number of > + * plotted graphs. > + */ > + _drawWindow.resize(dwWidth, > + _glWindow.height() + _legendAxisX.height()); > + > + /* Set the minimum height of the Graph widget. */ > + hMin = _drawWindow.height() + > + _pointerBar.height() + > + _navigationBar.height() + > + _layout.contentsMargins().top() + > + _layout.contentsMargins().bottom(); > + > + if (hMin > KS_GRAPH_HEIGHT * 8) > + hMin = KS_GRAPH_HEIGHT * 8; Why 8? (You are probably noticing a theme here ;-) > + > + setMinimumHeight(hMin); > + > + /* > + * Now use the height of the Draw Window to fix the maximum height > + * of the Graph widget. > + */ > + setMaximumHeight(_drawWindow.height() + > + _pointerBar.height() + > + _navigationBar.height() + > + _layout.spacing() * 2 + > + _layout.contentsMargins().top() + > + _layout.contentsMargins().bottom() + > + 2); /* Just a little bit of extra space. This will > + * allow the scroll bar to disappear when the > + * widget is extended to maximum. > + */ Ug, finish the 2); on the previous line, and keep the comment on its own line. -- Steve > +} > + > +void KsTraceGraph::_updateGraphLegends() > +{ > + QString graphLegends, graphName; > + QVBoxLayout *layout; > + int width = 0; > + > + if (_legendWindow.layout()) { > + /* > + * Remove and delete the existing layout of the legend window. > + */ > + QLayoutItem *child; > + while ((child = _legendWindow.layout()->takeAt(0)) != 0) { > + delete child->widget(); > + delete child; > + } > + > + delete _legendWindow.layout(); > + } > + > + layout = new QVBoxLayout; > + layout->setContentsMargins(FONT_WIDTH, 0, 0, 0); > + layout->setSpacing(_glWindow.vSpacing()); > + layout->setAlignment(Qt::AlignTop); > + layout->addSpacing(_glWindow.vMargin()); > + > + auto lamMakeName = [&]() { > + QLabel *name = new QLabel(graphName); > + > + if (width < STRING_WIDTH(graphName)) > + width = STRING_WIDTH(graphName); > + > + name->setAlignment(Qt::AlignBottom); > + name->setStyleSheet("QLabel {background-color : white;}"); > + name->setFixedHeight(KS_GRAPH_HEIGHT); > + layout->addWidget(name); > + }; > + > + for (auto const &cpu: _glWindow._cpuList) { > + graphName = QString("CPU %1").arg(cpu); > + lamMakeName(); > + } > + > + for (auto const &pid: _glWindow._taskList) { > + graphName = QString(tep_data_comm_from_pid(_data->tep(), > + pid)); > + graphName.append(QString("-%1").arg(pid)); > + lamMakeName(); > + } > + > + _legendWindow.setLayout(layout); > + _legendWindow.setMaximumWidth(width + FONT_WIDTH); > +}
![]() |