Files
DRAMSys/DRAMSys/traceAnalyzer/presentation/traceplot.cpp
Derek Christ ff50219c8a Make lines of TraceAnalyzer fully rearrangeable
This commit is a preparation for the upcoming feature that will make
dynamically rearranging the lines of the TracePlot possible. All
TracePlotLines can now be rearranged as wanted in the code.
2021-07-05 18:45:37 +02:00

684 lines
23 KiB
C++

/*
* Copyright (c) 2015, Technische Universität Kaiserslautern
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Authors:
* Janik Schlemminger
* Robert Gernhardt
* Matthias Jung
* Derek Christ
*/
#include <qwt_plot_grid.h>
#include <QWheelEvent>
#include <QMessageBox>
#include <QMouseEvent>
#include <iostream>
#include <QMenu>
#include <cmath>
#include <QInputDialog>
#include <QKeySequence>
#include <QFileInfo>
#include <QPushButton>
#include <qfiledialog.h>
#include <iostream>
#include <qwt_plot_renderer.h>
#include "tracePlotMouseLabel.h"
#include "traceplot.h"
#include "gototimedialog.h"
#include "tracedrawing.h"
#include "util/engineeringScaleDraw.h"
#include "util/clkgrid.h"
#include "util/customlabelscaledraw.h"
using namespace std;
TracePlot::TracePlot(QWidget *parent):
QwtPlot(parent), isInitialized(false),
customLabelScaleDraw(new CustomLabelScaleDraw(drawingProperties.getLabels()))
{
canvas()->setCursor(Qt::ArrowCursor);
setUpActions();
}
void TracePlot::setUpActions()
{
insertComment = new QAction("Insert comment", this);
QObject::connect(insertComment, SIGNAL(triggered()), this,
SLOT(on_insertComment()));
goToTime = new QAction("Go to time", this);
QObject::connect(goToTime, SIGNAL(triggered()), this, SLOT(on_goToTime()));
goToTransaction = new QAction("Go to transaction", this);
QObject::connect(goToTransaction, SIGNAL(triggered()), this,
SLOT(on_goToTransaction()));
deselectAll = new QAction("Deselect all", this);
QObject::connect(deselectAll, SIGNAL(triggered()), this,
SLOT(on_deselectAll()));
goToPhase = new QAction("Go to phase", this);
QObject::connect(goToPhase, SIGNAL(triggered()), this, SLOT(on_goToPhase()));
showQueryEditor = new QAction("Execute query", this);
showQueryEditor->setShortcut(QKeySequence("ctrl+e"));
addAction(showQueryEditor);
QObject::connect(showQueryEditor, SIGNAL(triggered()), this,
SLOT(on_executeQuery()));
selectNextRefresh = new QAction("Select next refresh", this);
selectNextRefresh->setShortcut(QKeySequence("alt+r"));
addAction(selectNextRefresh);
QObject::connect(selectNextRefresh, SIGNAL(triggered()), this,
SLOT(on_selectNextRefresh()));
selectNextActivate = new QAction("Select next activate", this);
selectNextActivate->setShortcut(QKeySequence("alt+a"));
addAction(selectNextActivate);
QObject::connect(selectNextActivate, SIGNAL(triggered()), this,
SLOT(on_selectNextActivate()));
selectNextPrecharge = new QAction("Select next precharge", this);
selectNextPrecharge->setShortcut(QKeySequence("alt+p"));
addAction(selectNextPrecharge);
QObject::connect(selectNextPrecharge, SIGNAL(triggered()), this,
SLOT(on_selectNextPrecharge()));
selectNextActb = new QAction("Select next atcb", this);
selectNextActb->setShortcut(QKeySequence("alt+b"));
addAction(selectNextActb);
QObject::connect(selectNextActb, SIGNAL(triggered()), this,
SLOT(on_selectNextActb()));
selectNextPreb = new QAction("Select next preb", this);
selectNextPreb->setShortcut(QKeySequence("alt+q"));
addAction(selectNextPreb);
QObject::connect(selectNextPreb, SIGNAL(triggered()), this,
SLOT(on_selectNextPreb()));
selectNextRefb = new QAction("Select next refb", this);
selectNextRefb->setShortcut(QKeySequence("alt+s"));
addAction(selectNextRefb);
QObject::connect(selectNextRefb, SIGNAL(triggered()), this,
SLOT(on_selectNextRefb()));
setColorGroupingPhase = new QAction("Group by Phase", this);
addAction(setColorGroupingPhase);
QObject::connect(setColorGroupingPhase, SIGNAL(triggered()), this,
SLOT(on_colorGroupingPhase()));
setColorGroupingTransaction = new QAction("Group by Transaction", this);
addAction(setColorGroupingTransaction);
QObject::connect(setColorGroupingTransaction, SIGNAL(triggered()), this,
SLOT(on_colorGroupingTransaction()));
setColorGroupingThread = new QAction("Group by Thread", this);
addAction(setColorGroupingThread);
QObject::connect(setColorGroupingThread, SIGNAL(triggered()), this,
SLOT(on_colorGroupingThread()));
exportToPdf = new QAction("Export to SVG", this);
addAction(exportToPdf);
QObject::connect(exportToPdf, SIGNAL(triggered()), this,
SLOT(on_exportToPDF()));
setUpContextMenu();
}
void TracePlot::setUpContextMenu()
{
contextMenu = new QMenu(this);
contextMenu->addActions({deselectAll});
QMenu *colorGroupingSubMenu = new QMenu("Group by", contextMenu);
colorGroupingSubMenu->addActions({setColorGroupingPhase, setColorGroupingTransaction, setColorGroupingThread});
contextMenu->addMenu(colorGroupingSubMenu);
QMenu *goToSubMenu = new QMenu("Go to", contextMenu);
goToSubMenu->addActions({goToPhase, goToTransaction, goToTime});
contextMenu->addMenu(goToSubMenu);
QMenu *selectSubMenu = new QMenu("Select", contextMenu);
selectSubMenu->addActions({selectNextRefresh, selectNextActivate, selectNextPrecharge, selectNextActb, selectNextPreb, selectNextRefb});
contextMenu->addMenu(selectSubMenu);
contextMenu->addActions({showQueryEditor, insertComment, exportToPdf});
}
void TracePlot::init(TraceNavigator *navigator, QScrollBar *scrollBar)
{
Q_ASSERT(isInitialized == false);
isInitialized = true;
this->scrollBar = scrollBar;
this->navigator = navigator;
connectNavigatorQ_SIGNALS();
setUpDrawingProperties();
setUpAxis();
setUpGrid();
setUpTracePlotItem();
setUpZoom();
setUpQueryEditor();
mouseLabel = new TracePlotMouseLabel(this,
navigator->GeneralTraceInfo().clkPeriod, this->mouseDownData.zoomSpan);
getAndDrawComments();
setZoomLevel(1000);
updateScrollbar();
replot();
}
void TracePlot::connectNavigatorQ_SIGNALS()
{
QObject::connect(navigator, SIGNAL(currentTraceTimeChanged()), this,
SLOT(currentTraceTimeChanged()));
QObject::connect(navigator, SIGNAL(selectedTransactionsChanged()), this,
SLOT(selectedTransactionsChanged()));
QObject::connect(navigator, SIGNAL(commentsChanged()), this,
SLOT(commentsChanged()));
}
void TracePlot::setUpDrawingProperties()
{
drawingProperties.init(navigator->getTracePlotLines(), this);
drawingProperties.numberOfRanks = navigator->GeneralTraceInfo().numberOfRanks;
drawingProperties.numberOfBankgroups = navigator->GeneralTraceInfo().numberOfBankgroups;
drawingProperties.numberOfBanks = navigator->GeneralTraceInfo().numberOfBanks;
drawingProperties.banksPerRank = drawingProperties.numberOfBanks / drawingProperties.numberOfRanks;
drawingProperties.groupsPerRank = drawingProperties.numberOfBankgroups / drawingProperties.numberOfRanks;
drawingProperties.banksPerGroup = drawingProperties.numberOfBanks / drawingProperties.numberOfBankgroups;
drawingProperties.setUpTracePlotLines();
}
void TracePlot::setUpQueryEditor()
{
queryEditor = new QueryEditor(this);
queryEditor->setWindowFlags(Qt::Window);
queryEditor->setWindowTitle("Query " + QFileInfo(
navigator->TraceFile().getPathToDB()).baseName());
queryEditor->init(navigator);
}
void TracePlot::setUpTracePlotItem()
{
tracePlotItem = new TracePlotItem(transactions, *navigator, drawingProperties);
tracePlotItem->setZ(1);
tracePlotItem->attach(this);
}
void TracePlot::setUpGrid()
{
unsigned int clk = navigator->GeneralTraceInfo().clkPeriod;
QwtPlotGrid *grid = new ClkGrid(clk, GridVisiblityClks * clk);
grid->setZ(0);
grid->attach(this);
}
void TracePlot::setUpZoom()
{
minZoomLevel = minZoomClks * navigator->GeneralTraceInfo().clkPeriod;
maxZoomLevel = maxZoomClks * navigator->GeneralTraceInfo().clkPeriod;
textVisibilityZoomLevel = textVisibilityClks *
navigator->GeneralTraceInfo().clkPeriod;
zoomZone = new QwtPlotZoneItem();
zoomZone->setZ(2);
zoomZone->attach(this);
zoomZone->setVisible(false);
}
void TracePlot::updateScrollbar()
{
// The maximum number of displayed lines determined by the pageStep of the scroll bar.
const unsigned int maxDisplayedLines = scrollBar->pageStep();
const int maximum = drawingProperties.getNumberOfDisplayedLines() - maxDisplayedLines;
if (maximum >= 0)
{
scrollBar->setMaximum(maximum);
scrollBar->show();
}
else
scrollBar->hide();
verticalScrollbarChanged(scrollBar->value());
}
void TracePlot::verticalScrollbarChanged(int value)
{
const int yMax = drawingProperties.getNumberOfDisplayedLines();
if (scrollBar->isHidden())
{
setAxisScale(yLeft, -1, yMax + 1, 1.0);
}
else
{
setAxisScale(yLeft, scrollBar->maximum() - 1 - value, yMax + 1 - value, 1.0);
}
replot();
}
void TracePlot::setUpAxis()
{
// Set up y axis.
setAxisScaleDraw(yLeft, customLabelScaleDraw);
customLabelScaleDraw->setMinimumExtent(135.0);
// Set up x axis.
setAxisTitle(xBottom, "Time in ns");
setAxisScaleDraw(xBottom, new EngineeringScaleDraw);
}
Timespan TracePlot::GetCurrentTimespan()
{
Timespan span(navigator->CurrentTraceTime() - zoomLevel / 2,
navigator->CurrentTraceTime() + zoomLevel / 2);
if (span.Begin() < 0)
span.shift(-span.Begin());
else if (span.End() > navigator->GeneralTraceInfo().span.End())
span.shift(navigator->GeneralTraceInfo().span.End() - span.End());
return span;
}
void TracePlot::getAndDrawComments()
{
for (const auto &pair : navigator->getComments()) {
const Comment &comment = pair.second;
QwtPlotMarker *maker = new QwtPlotMarker();
maker->setLabel(comment.Text());
maker->setLabelOrientation(Qt::Vertical);
maker->setLabelAlignment(Qt::AlignLeft | Qt::AlignBottom);
maker->setXValue(static_cast<double>(comment.Time()));
maker->setLineStyle(QwtPlotMarker::LineStyle::VLine);
maker->setLinePen(QColor(Qt::blue), 2);
maker->attach(this);
}
}
CustomLabelScaleDraw *TracePlot::getCustomLabelScaleDraw() const
{
return customLabelScaleDraw;
}
void TracePlot::enterZoomMode()
{
mouseDownData.mouseIsDownForZooming = true;
mouseLabel->setMode(MouseLabelMode::Timedifference);
zoomZone->setVisible(true);
zoomZone->setInterval(mouseDownData.zoomSpan.Begin(),
mouseDownData.zoomSpan.End());
}
void TracePlot::exitZoomMode()
{
mouseDownData.mouseIsDownForZooming = false;
mouseLabel->setMode(MouseLabelMode::AbsoluteTime);
zoomZone->setVisible(false);
}
void TracePlot::zoomIn(traceTime zoomCenter)
{
setZoomLevel(zoomLevel * zoomFactor);
traceTime time = zoomCenter + (GetCurrentTimespan().Middle() - zoomCenter) *
zoomFactor;
Q_EMIT tracePlotZoomChanged();
navigator->navigateToTime(time);
}
void TracePlot::zoomOut(traceTime zoomCenter)
{
setZoomLevel(zoomLevel / zoomFactor);
Q_EMIT tracePlotZoomChanged();
navigator->navigateToTime(static_cast<traceTime>(zoomCenter +
(GetCurrentTimespan().Middle() - zoomCenter) / zoomFactor));
}
void TracePlot::setZoomLevel(traceTime newZoomLevel)
{
zoomLevel = newZoomLevel;
if (zoomLevel < minZoomLevel)
zoomLevel = minZoomLevel;
if (zoomLevel > navigator->GeneralTraceInfo().span.timeCovered())
zoomLevel = navigator->GeneralTraceInfo().span.timeCovered();
if (zoomLevel > maxZoomLevel)
zoomLevel = maxZoomLevel;
if (zoomLevel < textVisibilityZoomLevel)
drawingProperties.drawText = true;
if (zoomLevel > textVisibilityZoomLevel)
drawingProperties.drawText = false;
}
/* Q_SLOTS
*
*
*/
void TracePlot::currentTraceTimeChanged()
{
transactions = navigator->TraceFile().getTransactionsInTimespan(
GetCurrentTimespan());
setAxisScale(xBottom, GetCurrentTimespan().Begin(), GetCurrentTimespan().End());
replot();
}
void TracePlot::selectedTransactionsChanged()
{
replot();
}
void TracePlot::commentsChanged()
{
detachItems(QwtPlotItem::Rtti_PlotMarker);
getAndDrawComments();
replot();
}
void TracePlot::on_executeQuery()
{
queryEditor->show();
}
void TracePlot::on_selectNextRefresh()
{
navigator->selectNextRefresh();
}
void TracePlot::on_selectNextActivate()
{
navigator->selectNextActivate();
}
void TracePlot::on_selectNextPrecharge()
{
navigator->selectNextPrecharge();
}
void TracePlot::on_selectNextActb()
{
navigator->selectNextActb();
}
void TracePlot::on_selectNextPreb()
{
navigator->selectNextPreb();
}
void TracePlot::on_selectNextRefb()
{
navigator->selectNextRefb();
}
void TracePlot::on_colorGroupingPhase()
{
drawingProperties.colorGrouping = ColorGrouping::PhaseType;
Q_EMIT(colorGroupingChanged(ColorGrouping::PhaseType));
replot();
}
void TracePlot::on_colorGroupingTransaction()
{
drawingProperties.colorGrouping = ColorGrouping::Transaction;
Q_EMIT(colorGroupingChanged(ColorGrouping::Transaction));
replot();
}
void TracePlot::on_colorGroupingThread()
{
drawingProperties.colorGrouping = ColorGrouping::Thread;
Q_EMIT(colorGroupingChanged(ColorGrouping::Thread));
replot();
}
void TracePlot::on_goToTransaction()
{
bool ok;
int maxID = navigator->GeneralTraceInfo().numberOfTransactions;
int transactionID = QInputDialog::getInt(this, "Go to transaction",
"Enter transaction ID (1 - " + QString::number(maxID) + ")", 0, 1, maxID, 1,
&ok);
if (ok) {
navigator->clearSelectedTransactions();
navigator->selectTransaction(transactionID);
}
}
void TracePlot::on_goToPhase()
{
bool ok;
int maxID = navigator->GeneralTraceInfo().numberOfPhases;
int phaseID = QInputDialog::getInt(this, "Go to phase",
"Enter phase ID (1 - " + QString::number(maxID) + ")", 0, 1, maxID, 1, &ok);
if (ok) {
navigator->clearSelectedTransactions();
navigator->selectTransaction(navigator->TraceFile().getTransactionIDFromPhaseID(
phaseID));
}
}
void TracePlot::on_deselectAll()
{
navigator->clearSelectedTransactions();
}
void TracePlot::on_insertComment()
{
traceTime time = invTransform(xBottom, contextMenuMouseDown.x());
bool ok;
QString text = QInputDialog::getText(this, QString("Insert comment"),
QString("Insert comment at ") + prettyFormatTime(time), QLineEdit::Normal,
QString(), &ok);
if (ok) {
navigator->insertComment(Comment(time, text));
}
}
void TracePlot::on_goToTime()
{
double goToTime;
GoToTimeDialog dialog(&goToTime, this);
int dialogCode = dialog.exec();
if (dialogCode == QDialog::Accepted) {
traceTime time = static_cast<traceTime>(goToTime) * 1000;
navigator->navigateToTime(time);
}
}
void TracePlot::on_exportToPDF()
{
QwtPlotRenderer renderer;
QString filename = QFileDialog::getSaveFileName(this, "Export to SVG", "",
"Portable Document Format(*.svg)");
if (filename != "") {
QBrush saved = this->canvasBackground();
this->setCanvasBackground(QBrush(Qt::white));
renderer.renderDocument(this, filename, "svg", QSizeF(this->widthMM(),
this->heightMM()));
this->setCanvasBackground(QBrush(saved));
}
}
/* Keyboard and mouse events
*
*
*/
void TracePlot::keyPressEvent(QKeyEvent *keyPressedEvent)
{
int key = keyPressedEvent->key();
if (Qt::Key_Control == key)
keyPressData.ctrlPressed = true;
else if (Qt::Key_Shift == key)
keyPressData.shiftPressed = true;
else if (Qt::Key_Right == key)
navigator->selectNextTransaction();
else if (Qt::Key_Left == key)
navigator->selectPreviousTransaction();
else if (Qt::Key_Minus == key) {
zoomOut(GetCurrentTimespan().Middle());
} else if (Qt::Key_Plus == key) {
zoomIn(GetCurrentTimespan().Middle());
}
}
void TracePlot::keyReleaseEvent(QKeyEvent *keyReleasedEvent)
{
int key = keyReleasedEvent->key();
if (Qt::Key_Control == key)
keyPressData.ctrlPressed = false;
else if (Qt::Key_Shift == key) {
keyPressData.shiftPressed = false;
exitZoomMode();
replot();
}
}
bool TracePlot::eventFilter(QObject *object, QEvent *event)
{
if (object == canvas()) {
switch (event->type()) {
case QEvent::Wheel : {
QWheelEvent *wheelEvent = static_cast<QWheelEvent *>(event);
traceTime zoomCenter = static_cast<traceTime>(this->invTransform(xBottom,
wheelEvent->x()));
(wheelEvent->delta() > 0) ? zoomIn(zoomCenter) : zoomOut(zoomCenter);
return true;
}
case QEvent::MouseButtonPress: {
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
if (mouseEvent->button() == Qt::LeftButton) {
if (keyPressData.shiftPressed) {
mouseDownData.zoomSpan.setBegin(alignToClk(invTransform(xBottom,
mouseEvent->x()), navigator->GeneralTraceInfo().clkPeriod));
mouseDownData.zoomSpan.setEnd(alignToClk(invTransform(xBottom, mouseEvent->x()),
navigator->GeneralTraceInfo().clkPeriod));
enterZoomMode();
} else {
mouseDownData.mouseDownX = mouseEvent->x();
mouseDownData.mouseDownTime = GetCurrentTimespan().Middle();
mouseDownData.mouseIsDownForDragging = true;
canvas()->setCursor(Qt::ClosedHandCursor);
SelectTransaction(mouseEvent->x(), mouseEvent->y());
}
return true;
} else if (mouseEvent->button() == Qt::RightButton) {
openContextMenu(this->canvas()->mapToGlobal(mouseEvent->pos()),
mouseEvent->pos());
return true;
}
break;
}
case QEvent::MouseButtonRelease: {
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
if (mouseEvent->button() == Qt::LeftButton) {
if (mouseDownData.mouseIsDownForDragging) {
mouseDownData.mouseIsDownForDragging = false;
canvas()->setCursor(Qt::ArrowCursor);
return true;
} else if (mouseDownData.mouseIsDownForZooming) {
exitZoomMode();
traceTime newCenter = mouseDownData.zoomSpan.Middle();
setZoomLevel(mouseDownData.zoomSpan.timeCovered());
navigator->navigateToTime(newCenter);
replot();
return true;
}
}
break;
}
case QEvent::MouseMove: {
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
if (mouseDownData.mouseIsDownForDragging) {
traceTime deltaTime = invTransform(xBottom,
mouseDownData.mouseDownX) - invTransform(xBottom, mouseEvent->x());
navigator->navigateToTime(mouseDownData.mouseDownTime + deltaTime);
return true;
} else if (mouseDownData.mouseIsDownForZooming) {
mouseDownData.zoomSpan.setEnd(alignToClk(invTransform(xBottom, mouseEvent->x()),
navigator->GeneralTraceInfo().clkPeriod));
if (mouseDownData.zoomSpan.Begin() < mouseDownData.zoomSpan.End())
zoomZone->setInterval(mouseDownData.zoomSpan.Begin(),
mouseDownData.zoomSpan.End());
else
zoomZone->setInterval(mouseDownData.zoomSpan.End(),
mouseDownData.zoomSpan.Begin());
replot();
}
break;
}
default:
break;
}
}
return false;
}
void TracePlot::SelectTransaction(int x, int y)
{
double yVal = invTransform(yLeft, y);
traceTime time = invTransform(xBottom, x);
vector<shared_ptr<Transaction>> selectedTransactions =
tracePlotItem->getSelectedTransactions(time, yVal);
if (selectedTransactions.size() > 0) {
if (!keyPressData.ctrlPressed)
navigator->clearSelectedTransactions();
navigator->addSelectedTransactions(selectedTransactions);
}
}
void TracePlot::openContextMenu(const QPoint &pos, const QPoint &mouseDown)
{
contextMenuMouseDown = mouseDown;
contextMenu->exec(pos);
}