/* * 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 * Iron Prando da Silva */ #include "tracefiletab.h" #include "businessObjects/commentmodel.h" #include "businessObjects/configmodels.h" #include "businessObjects/dramTimeDependencies/phasedependenciestracker.h" #include "businessObjects/pythoncaller.h" #include "businessObjects/traceplotlinemodel.h" #include "businessObjects/tracetime.h" #include "queryeditor.h" #include "traceanalyzer.h" #include "ui_tracefiletab.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include TraceFileTab::TraceFileTab(std::string_view traceFilePath, PythonCaller &pythonCaller, QWidget *parent) : QWidget(parent), ui(new Ui::TraceFileTab), commentModel(new CommentModel(this)), navigator(new TraceNavigator(traceFilePath.data(), commentModel, this)), mcConfigModel(new McConfigModel(navigator->TraceFile(), this)), memSpecModel(new MemSpecModel(navigator->TraceFile(), this)), availableRowsModel(new AvailableTracePlotLineModel(navigator->GeneralTraceInfo(), this)), selectedRowsModel(new SelectedTracePlotLineModel(navigator->GeneralTraceInfo(), this)), tracePlotLineDataSource(new TracePlotLineDataSource(selectedRowsModel, this)), depInfosView(new DependencyInfosModel(navigator->TraceFile(), this)), savingChangesToDB(false), pythonCaller(pythonCaller), traceFilePath(traceFilePath) { ui->setupUi(this); std::cout << "Opening new tab for \"" << traceFilePath << "\"" << std::endl; ui->mcConfigView->setModel(mcConfigModel); ui->mcConfigView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); ui->memSpecView->setModel(memSpecModel); ui->memSpecView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); ui->depInfosView->setModel(depInfosView); ui->depInfosView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); setUpTraceSelector(); initNavigatorAndItsDependentWidgets(traceFilePath.data()); setUpFileWatcher(traceFilePath.data()); setUpTraceplotScrollbar(); setUpCommentView(); setUpPossiblePhases(); tracefileChanged(); } TraceFileTab::~TraceFileTab() { delete ui; } void TraceFileTab::commitChangesToDB() { savingChangesToDB = true; navigator->commitChangesToDB(); } void TraceFileTab::exportAsVCD() { QString filename = QFileDialog::getSaveFileName(this, "Export to VCD", "", "VCD files (*.vcd)"); auto dumpVcd = [=]() { QString dump = pythonCaller.exportAsVcd(traceFilePath.data()); QFile file(filename); file.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out(&file); out << dump; file.close(); Q_EMIT statusChanged(QString("VCD export finished.")); }; if (filename != "") { QtConcurrent::run(dumpVcd); } } void TraceFileTab::setUpTraceSelector() { ui->availableTreeView->setModel(availableRowsModel); ui->availableTreeView->setSelectionModel(availableRowsModel->selectionModel()); ui->availableTreeView->installEventFilter(availableRowsModel); ui->selectedTreeView->setModel(selectedRowsModel); ui->selectedTreeView->setSelectionModel(selectedRowsModel->selectionModel()); ui->selectedTreeView->installEventFilter(selectedRowsModel); connect(availableRowsModel, &AvailableTracePlotLineModel::returnPressed, selectedRowsModel, &SelectedTracePlotLineModel::addIndexesFromAvailableModel); connect(ui->availableTreeView, &QAbstractItemView::doubleClicked, availableRowsModel, &AvailableTracePlotLineModel::itemsDoubleClicked); connect(ui->selectedTreeView, &QAbstractItemView::doubleClicked, selectedRowsModel, &SelectedTracePlotLineModel::itemsDoubleClicked); connect(selectedRowsModel, &QAbstractItemModel::dataChanged, tracePlotLineDataSource, &TracePlotLineDataSource::updateModel); connect(selectedRowsModel, &QAbstractItemModel::rowsInserted, tracePlotLineDataSource, &TracePlotLineDataSource::updateModel); connect(selectedRowsModel, &QAbstractItemModel::rowsRemoved, tracePlotLineDataSource, &TracePlotLineDataSource::updateModel); } void TraceFileTab::setUpTraceplotScrollbar() { QObject::connect(ui->traceplotScrollbar, SIGNAL(valueChanged(int)), ui->traceplot, SLOT(verticalScrollbarChanged(int))); } void TraceFileTab::initNavigatorAndItsDependentWidgets(QString path) { ui->traceplot->init(navigator, ui->traceplotScrollbar, tracePlotLineDataSource, commentModel); ui->traceScroller->init(navigator, ui->traceplot, tracePlotLineDataSource); connect(this, SIGNAL(colorGroupingChanged(ColorGrouping)), ui->traceScroller, SLOT(colorGroupingChanged(ColorGrouping))); ui->selectedTransactionTree->init(navigator); //ui->debugMessages->init(navigator,ui->traceplot); ui->bandwidthPlot->canvas()->installEventFilter(this); ui->powerPlot->canvas()->installEventFilter(this); ui->bufferPlot->canvas()->installEventFilter(this); } void TraceFileTab::setUpFileWatcher(QString path) { fileWatcher = new QFileSystemWatcher(QStringList(path), this); QObject::connect(fileWatcher, SIGNAL(fileChanged(QString)), this, SLOT(tracefileChanged())); } void TraceFileTab::setUpCommentView() { ui->commentView->setModel(commentModel); ui->commentView->setSelectionModel(commentModel->selectionModel()); ui->commentView->installEventFilter(commentModel); ui->commentView->setContextMenuPolicy(Qt::CustomContextMenu); QObject::connect(ui->commentView, &QTableView::customContextMenuRequested, commentModel, &CommentModel::openContextMenu); QObject::connect(commentModel, &CommentModel::editTriggered, ui->commentView, [=](const QModelIndex &index) { ui->tabWidget->setCurrentWidget(ui->tabComments); ui->commentView->edit(index); ui->commentView->scrollTo(index); }); QObject::connect(ui->commentView, &QTableView::doubleClicked, commentModel, &CommentModel::rowDoubleClicked); } void TraceFileTab::setUpPossiblePhases() { const auto possiblePhases = ConfigurationFactory::possiblePhases(navigator->TraceFile()); for (auto p : possiblePhases) { auto item = new QListWidgetItem(p, ui->depTabPossiblePhases); item->setFlags(item->flags() | Qt::ItemIsUserCheckable); // set checkable flag item->setCheckState(Qt::Unchecked); // AND initialize check state } ui->calculateDependencies->setEnabled(ConfigurationFactory::deviceSupported(navigator->TraceFile())); } void TraceFileTab::tracefileChanged() { if (savingChangesToDB == true) { // Database has changed due to user action (e.g., saving comments). // No need to disable the "Save changes to DB" menu. savingChangesToDB = false; Q_EMIT statusChanged(QString("Changes saved ")); } else { // External event changed the database file (e.g., the database file // was overwritten when running a new test). // The "Save changes to DB" menu must be disabled to avoid saving // changes to a corrupted or inconsistent file. Q_EMIT statusChanged(QString("At least one database has changed on disk ")); } navigator->refreshData(); } void TraceFileTab::closeEvent(QCloseEvent *event) { if (navigator->existChangesToCommit()) { QMessageBox saveDialog; saveDialog.setWindowTitle(QFileInfo(traceFilePath.data()).baseName()); saveDialog.setText("The trace file has been modified."); saveDialog.setInformativeText("Do you want to save your changes?
Unsaved changes will be lost."); saveDialog.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); saveDialog.setDefaultButton(QMessageBox::Save); saveDialog.setIcon(QMessageBox::Warning); int returnCode = saveDialog.exec(); switch (returnCode) { case QMessageBox::Cancel: event->ignore(); break; case QMessageBox::Discard: event->accept(); break; case QMessageBox::Save: commitChangesToDB(); event->accept(); break; }; } else event->accept(); } traceTime TraceFileTab::getCurrentTraceTime() const { return navigator->CurrentTraceTime(); } void TraceFileTab::navigateToTime(traceTime time) { navigator->navigateToTime(time); } traceTime TraceFileTab::getZoomLevel() const { TracePlot *traceplot = static_cast(ui->traceplot); return traceplot->ZoomLevel(); } void TraceFileTab::setZoomLevel(traceTime zoomLevel) { TracePlot *traceplot = static_cast(ui->traceplot); TraceScroller *tracescroller = static_cast(ui->traceScroller); traceplot->setZoomLevel(zoomLevel); tracescroller->tracePlotZoomChanged(); } std::shared_ptr TraceFileTab::saveTraceSelectorState() const { return selectedRowsModel->getClonedRootNode(); } void TraceFileTab::restoreTraceSelectorState(std::shared_ptr rootNode) { selectedRowsModel->setRootNode(std::move(rootNode)); } class ItemDelegate: public QItemDelegate { public: ItemDelegate(QObject* parent = nullptr): QItemDelegate(parent) { } void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { if (index.column() == 1) { double progress = index.data().toDouble(); QStyleOptionProgressBar opt; opt.rect = option.rect; opt.minimum = 0; opt.maximum = 100; opt.progress = static_cast(floor(progress)); opt.text = QString::number(progress, 'f', 2)+" %"; opt.textVisible = true; QApplication::style()->drawControl(QStyle::CE_ProgressBar, &opt, painter, nullptr); } else { QItemDelegate::paint(painter, option, index); } } }; void TraceFileTab::on_latencyTreeView_doubleClicked(const QModelIndex &index) { // Get onlye the leaf: if(index.column() == 0 && index.model()->hasChildren(index) == false) { unsigned int id = index.data().toUInt(); if(id!=0) { navigator->selectTransaction(id); } } } bool TraceFileTab::eventFilter(QObject *object, QEvent *event) { if (auto canvas = qobject_cast(object)) { if (event->type() == QEvent::MouseButtonDblClick) { QMouseEvent *mouseEvent = static_cast(event); if (mouseEvent->button() != Qt::LeftButton) return false; QwtPlot *plot = canvas->plot(); double realTime = plot->invTransform(QwtPlot::xBottom, mouseEvent->x()); // Convert from seconds to picoseconds traceTime time = realTime * 1000 * 1000 * 1000 * 1000; navigator->navigateToTime(time); return true; } } return QWidget::eventFilter(object, event); } void TraceFileTab::on_calculateDependencies_clicked() { std::vector dependencyFilter; for (int row = 0; row < ui->depTabPossiblePhases->count(); row++) { auto item = ui->depTabPossiblePhases->item(row); if (item->checkState() == Qt::Checked) dependencyFilter.push_back(item->text()); } savingChangesToDB = true; PhaseDependenciesTracker::calculateDependencies(navigator->TraceFile(), dependencyFilter); depInfosView = new DependencyInfosModel(navigator->TraceFile(), this); ui->depInfosView->setModel(depInfosView); ui->depInfosView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); } void TraceFileTab::on_startLatencyAnalysis_clicked() { // Setup Database: QSqlDatabase db = navigator->TraceFile().getDatabase(); QSqlQuery query(db); // Check the count of transactions: QString sql = "SELECT COUNT(*) FROM Transactions;"; query.exec(sql); query.next(); int maxTransactions = query.value(0).toInt(); // Create Database Setup and Query: sql = "SELECT ((p2.PhaseEnd - p1.PhaseBegin)/1000) as latency, t.id " "FROM Transactions t, Phases p1, Phases p2 " "WHERE t.id = p1.Transact " "AND t.id = p2.Transact " "AND p1.PhaseName = \"REQ\" " "AND p2.PhaseName = \"RESP\" ORDER BY latency;"; query.exec(sql); // Creatoe model and fill it from Database: QStandardItemModel* model = new QStandardItemModel(); int currentLatency = 0; QStandardItem* currentLatencyItem = nullptr; int counter = 0; while (query.next()) { if(query.value(0) != currentLatency) { currentLatencyItem = new QStandardItem(QString::number(query.value(0).toInt())+" ns"); currentLatency = query.value(0).toInt(); QList row; row.append(currentLatencyItem); row.append(new QStandardItem()); model->appendRow(row); } QStandardItem * id = new QStandardItem(query.value(1).toString()); currentLatencyItem->appendRow(id); counter++; int percentage = int(ceil((double(counter))/(double(maxTransactions))*100.0)); ui->latencyAnalysisProgressBar->setValue(percentage); } QStringList header = {"Latency","Occurences"}; model->setHorizontalHeaderLabels(header); // Generate Histrogram and Tree: QwtPlotHistogram *hist = new QwtPlotHistogram; QVector *intervals = new QVector; for(int i = 0; i < model->rowCount(); i++) { double latency = model->item(i,0)->text().replace(" ns","").toDouble(); int numberOfChilds = model->item(i)->rowCount(); double percentage = 100*((double(numberOfChilds))/(double(counter))); model->item(i,1)->setText(QString::number(percentage)); intervals->append(QwtIntervalSample(percentage, latency, latency+1)); } ui->latencyTreeView->setItemDelegate(new ItemDelegate(ui->latencyTreeView)); ui->latencyTreeView->setModel(model); hist->setSamples(*intervals); hist->attach(ui->latencyPlot); hist->setPen(QPen(QColor(255,0,0,100))); hist->setBrush(QBrush(QColor(255,0,0,255))); ui->latencyPlot->setAxisTitle(0,"Occurences [%]"); QwtText axisTitle( "Latency [ns]" ); axisTitle.setFont( ui->latencyPlot->axisTitle( QwtPlot::xBottom ).font() ); ui->latencyPlot->setAxisTitle( QwtPlot::xBottom, axisTitle ); ui->latencyPlot->replot(); } void TraceFileTab::on_startPowerAnalysis_clicked() { qDebug() << "Power Analysis"; QSqlDatabase db = navigator->TraceFile().getDatabase(); QSqlQuery query(db); QString sql = "SELECT time, AveragePower FROM Power;"; query.exec(sql); QwtPointSeriesData * data = new QwtPointSeriesData; QwtPlotCurve * cur = new QwtPlotCurve("Speed"); QVector* samples=new QVector; while (query.next()) { double time = query.value(0).toDouble(); double power = query.value(1).toDouble(); samples->push_back(QPointF(time,power)); } //ui->powerPlot->setAxisTitle(QwtPlot::xBottom,"Time"); //ui->powerPlot->setAxisLabelRotation(QwtPlot::xBottom,-50.0); //ui->powerPlot->setAxisLabelAlignment(QwtPlot::xBottom,Qt::AlignLeft|Qt::AlignBottom); //ui->powerPlot->setAxisTitle(QwtPlot::yLeft,"Power"); data->setSamples(*samples); cur->setData(data); cur->attach(ui->powerPlot); cur->setPen(QPen(QColor(255,0,0))); QwtPlotMagnifier *mag1 = new QwtPlotMagnifier(ui->powerPlot->canvas()); mag1->setAxisEnabled(QwtPlot::xBottom, true); mag1->setAxisEnabled(QwtPlot::yLeft, false); mag1->setWheelFactor(5); QwtPlotPanner *pan1 = new QwtPlotPanner(ui->powerPlot->canvas()); pan1->setAxisEnabled(QwtPlot::xBottom, true); pan1->setAxisEnabled(QwtPlot::yLeft, false); ui->powerPlot->replot(); // Bandwidth analysis: sql = "SELECT time, AverageBandwidth FROM Bandwidth;"; query.exec(sql); QwtPointSeriesData * data2 = new QwtPointSeriesData; QwtPlotCurve * cur2 = new QwtPlotCurve("Speed"); QVector* samples2=new QVector; while (query.next()) { double time = query.value(0).toDouble(); double percentage = query.value(1).toDouble() * 100.0; samples2->push_back(QPointF(time, percentage)); } data2->setSamples(*samples2); cur2->setData(data2); cur2->attach(ui->bandwidthPlot); cur2->setPen(QPen(QColor(255,0,0))); ui->bandwidthPlot->setAxisTitle(0,"Bandwidth [%]"); ui->bandwidthPlot->setAxisScale(0,0.0,100.0); QwtText axisTitle2( "Time [s]" ); axisTitle2.setFont( ui->bandwidthPlot->axisTitle( QwtPlot::xBottom ).font() ); ui->bandwidthPlot->setAxisTitle( QwtPlot::xBottom, axisTitle2 ); QwtPlotMagnifier *mag2 = new QwtPlotMagnifier(ui->bandwidthPlot->canvas()); mag2->setAxisEnabled(QwtPlot::xBottom, true); mag2->setAxisEnabled(QwtPlot::yLeft, false); mag2->setWheelFactor(5); QwtPlotPanner *pan2 = new QwtPlotPanner(ui->bandwidthPlot->canvas()); pan2->setAxisEnabled(QwtPlot::xBottom, true); pan2->setAxisEnabled(QwtPlot::yLeft, false); ui->bandwidthPlot->replot(); // Buffer analysis: sql = "select max(BufferNumber) from BufferDepth;"; query.exec(sql); query.next(); unsigned int numberOfBuffers = query.value(0).toUInt(); sql = "select MaxBufferDepth from GeneralInfo;"; query.exec(sql); query.next(); unsigned int maxBufferDepth = query.value(0).toUInt(); sql = "select Time, AverageBufferDepth from BufferDepth where BufferNumber = 0"; // TODO query.exec(sql); QwtPointSeriesData * data3 = new QwtPointSeriesData; QwtPlotCurve * cur3 = new QwtPlotCurve("Speed"); QVector* samples3 = new QVector; while (query.next()) { double time = query.value(0).toDouble(); double queue = query.value(1).toDouble(); samples3->push_back(QPointF(time, queue)); } data3->setSamples(*samples3); cur3->setData(data3); cur3->attach(ui->bufferPlot); cur3->setPen(QPen(QColor(255,0,0))); ui->bufferPlot->setAxisTitle(0,"Buffer Utilization"); ui->bufferPlot->setAxisScale(0,0.0, maxBufferDepth); QwtText axisTitle3( "Time [s]" ); axisTitle3.setFont( ui->bufferPlot->axisTitle( QwtPlot::xBottom ).font() ); ui->bufferPlot->setAxisTitle( QwtPlot::xBottom, axisTitle3 ); QwtPlotMagnifier *mag3 = new QwtPlotMagnifier(ui->bufferPlot->canvas()); mag3->setAxisEnabled(QwtPlot::xBottom, true); mag3->setAxisEnabled(QwtPlot::yLeft, false); mag3->setWheelFactor(5); QwtPlotPanner *pan3 = new QwtPlotPanner(ui->bufferPlot->canvas()); pan3->setAxisEnabled(QwtPlot::xBottom, true); pan3->setAxisEnabled(QwtPlot::yLeft, false); ui->bufferPlot->replot(); }