diff --git a/.selfcheck_suppressions b/.selfcheck_suppressions index 764b8818308..cdac1ba33ab 100644 --- a/.selfcheck_suppressions +++ b/.selfcheck_suppressions @@ -12,7 +12,7 @@ funcArgNamesDifferent:*/moc_resultsview.cpp funcArgNamesDifferent:*/moc_threadhandler.cpp funcArgNamesDifferent:*/moc_threadresult.cpp naming-varname:*/gui/ui_*.h -functionStatic:*/ui_fileview.h +functionStatic:*/gui/ui_*.h # --debug-warnings suppressions valueFlowBailout diff --git a/gui/checkthread.cpp b/gui/checkthread.cpp index 8f042068ee9..2a9e092cc8c 100644 --- a/gui/checkthread.cpp +++ b/gui/checkthread.cpp @@ -105,8 +105,9 @@ int CheckThread::executeCommand(std::string exe, std::vector args, } -CheckThread::CheckThread(ThreadResult &result) : - mResult(result) +CheckThread::CheckThread(ThreadResult &result, int index) + : mResult(result) + , mThreadIndex(index) {} void CheckThread::setSettings(const Settings &settings, std::shared_ptr supprs) @@ -147,9 +148,14 @@ void CheckThread::run() while (file && mState == Running) { const std::string& fname = file->spath(); qDebug() << "Checking file" << QString::fromStdString(fname); + + const Details details{ mThreadIndex, QString::fromStdString(fname), QTime::currentTime(), }; + emit startCheck(details); + cppcheck.check(*file); runAddonsAndTools(mSettings, nullptr, QString::fromStdString(fname)); - emit fileChecked(QString::fromStdString(fname)); + + emit finishCheck(details); if (mState == Running) mResult.getNextFile(file); @@ -160,9 +166,15 @@ void CheckThread::run() while (fileSettings && mState == Running) { const std::string& fname = fileSettings->filename(); qDebug() << "Checking file" << QString::fromStdString(fname); - cppcheck.check(*fileSettings); + + const Details details{ mThreadIndex, QString::fromStdString(fname), QTime::currentTime(), }; + emit startCheck(details); + + cppcheck.check(*file); runAddonsAndTools(mSettings, fileSettings, QString::fromStdString(fname)); - emit fileChecked(QString::fromStdString(fname)); + + emit finishCheck(details); + if (mState == Running) mResult.getNextFileSettings(fileSettings); @@ -486,3 +498,4 @@ QString CheckThread::clangTidyCmd() return QString(); } + diff --git a/gui/checkthread.h b/gui/checkthread.h index 4247a45094c..ef952b98b6b 100644 --- a/gui/checkthread.h +++ b/gui/checkthread.h @@ -36,6 +36,7 @@ #include #include #include +#include class ThreadResult; @@ -48,8 +49,16 @@ class ThreadResult; */ class CheckThread : public QThread { Q_OBJECT + +public: + struct Details { + int threadIndex; + QString file; + QTime startTime; + }; + public: - explicit CheckThread(ThreadResult &result); + CheckThread(ThreadResult &result, int index); /** * @brief Set settings for cppcheck @@ -102,8 +111,8 @@ class CheckThread : public QThread { */ void done(); - // NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name) - caused by generated MOC code - void fileChecked(const QString &file); + void startCheck(CheckThread::Details details); + void finishCheck(CheckThread::Details details); protected: /** @@ -126,6 +135,7 @@ class CheckThread : public QThread { std::atomic mState{Ready}; ThreadResult &mResult; + int mThreadIndex{}; Settings mSettings; std::shared_ptr mSuppressions; diff --git a/gui/gui.pro b/gui/gui.pro index 132f113d270..8706e86c86e 100644 --- a/gui/gui.pro +++ b/gui/gui.pro @@ -70,7 +70,8 @@ FORMS = about.ui \ librarydialog.ui \ libraryaddfunctiondialog.ui \ libraryeditargdialog.ui \ - newsuppressiondialog.ui + newsuppressiondialog.ui \ + threaddetails.ui TRANSLATIONS = cppcheck_de.ts \ cppcheck_es.ts \ @@ -156,6 +157,7 @@ HEADERS += aboutdialog.h \ settingsdialog.h \ showtypes.h \ statsdialog.h \ + threaddetails.h \ threadhandler.h \ threadresult.h \ translationhandler.h \ @@ -199,6 +201,7 @@ SOURCES += aboutdialog.cpp \ settingsdialog.cpp \ showtypes.cpp \ statsdialog.cpp \ + threaddetails.cpp \ threadhandler.cpp \ threadresult.cpp \ translationhandler.cpp \ diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 19270f1605b..4cb7f8d3687 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -53,6 +53,7 @@ #include "threadresult.h" #include "translationhandler.h" #include "utils.h" +#include "threaddetails.h" #include "ui_mainwindow.h" @@ -183,6 +184,7 @@ MainWindow::MainWindow(TranslationHandler* th, QSettings* settings) : connect(mUI->mActionShowHidden, &QAction::triggered, mUI->mResults, &ResultsView::showHiddenResults); connect(mUI->mActionViewStats, &QAction::triggered, this, &MainWindow::showStatistics); connect(mUI->mActionLibraryEditor, &QAction::triggered, this, &MainWindow::showLibraryEditor); + connect(mUI->mActionShowThreadDetails, &QAction::triggered, this, &MainWindow::showThreadDetails); connect(mUI->mActionReanalyzeModified, &QAction::triggered, this, &MainWindow::reAnalyzeModified); connect(mUI->mActionReanalyzeAll, &QAction::triggered, this, &MainWindow::reAnalyzeAll); @@ -2109,6 +2111,16 @@ void MainWindow::showLibraryEditor() libraryDialog.exec(); } +void MainWindow::showThreadDetails() +{ + auto *td = new ThreadDetails(this); + connect(mThread, &ThreadHandler::threadDetailsUpdated, + td, &ThreadDetails::threadDetailsUpdated, Qt::QueuedConnection); + td->setAttribute(Qt::WA_DeleteOnClose); + td->show(); + mThread->emitThreadDetailsUpdated(); +} + void MainWindow::filterResults() { mUI->mResults->filterResults(mLineEditFilter->text()); diff --git a/gui/mainwindow.h b/gui/mainwindow.h index 76fda4f0de6..e29d4d48f5b 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -200,6 +200,9 @@ public slots: /** @brief Slot for showing the library editor */ void showLibraryEditor(); + /** @brief Slot for showing the thread details window */ + void showThreadDetails(); + private slots: /** @brief Slot for checkthread's done signal */ diff --git a/gui/mainwindow.ui b/gui/mainwindow.ui index a90949344e2..2e30c8c10be 100644 --- a/gui/mainwindow.ui +++ b/gui/mainwindow.ui @@ -64,7 +64,7 @@ - QLayout::SetDefaultConstraint + QLayout::SizeConstraint::SetDefaultConstraint @@ -84,13 +84,13 @@ Checking for updates - Qt::RichText + Qt::TextFormat::RichText true - Qt::TextBrowserInteraction + Qt::TextInteractionFlag::TextBrowserInteraction @@ -104,7 +104,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -124,7 +124,7 @@ 0 0 640 - 22 + 21 @@ -192,6 +192,7 @@ + @@ -217,8 +218,6 @@ - - @@ -227,9 +226,6 @@ - - - @@ -813,30 +809,6 @@ C&11 - - - true @@ -947,28 +919,6 @@ C++20 - - Compliance report... @@ -1040,6 +990,14 @@ EULA... + + + Thread Details + + + Show thread details + + diff --git a/gui/test/resultstree/testresultstree.cpp b/gui/test/resultstree/testresultstree.cpp index ac3ba4ff78c..f05d61bc824 100644 --- a/gui/test/resultstree/testresultstree.cpp +++ b/gui/test/resultstree/testresultstree.cpp @@ -93,6 +93,14 @@ void ThreadHandler::stop() { void ThreadHandler::threadDone() { throw 1; } +// NOLINTBEGIN(performance-unnecessary-value-param) +void ThreadHandler::startCheck(CheckThread::Details /*unused*/) { + throw 1; +} +void ThreadHandler::finishCheck(CheckThread::Details /*unused*/) { + throw 1; +} +// NOLINTEND(performance-unnecessary-value-param) Application& ApplicationList::getApplication(const int /*unused*/) { throw 1; } @@ -110,7 +118,8 @@ QString XmlReport::unquoteMessage(const QString &message) { return message; } XmlReport::XmlReport(const QString& filename) : Report(filename) {} -void ThreadResult::fileChecked(const QString & /*unused*/) { +// NOLINTNEXTLINE(performance-unnecessary-value-param) +void ThreadResult::finishCheck(CheckThread::Details /*unused*/) { throw 1; } void ThreadResult::reportOut(const std::string & /*unused*/, Color /*unused*/) { diff --git a/gui/threaddetails.cpp b/gui/threaddetails.cpp new file mode 100644 index 00000000000..fe0da86362f --- /dev/null +++ b/gui/threaddetails.cpp @@ -0,0 +1,21 @@ +#include "threaddetails.h" +#include "ui_threaddetails.h" + +ThreadDetails::ThreadDetails(QWidget *parent) + : QWidget(parent) + , mUi(new Ui::ThreadDetails) +{ + setWindowFlags(Qt::Window); + mUi->setupUi(this); + mUi->detailsBox->setReadOnly(true); +} + +ThreadDetails::~ThreadDetails() +{ + delete mUi; +} + +void ThreadDetails::threadDetailsUpdated(const QString &text) +{ + mUi->detailsBox->setText(text); +} diff --git a/gui/threaddetails.h b/gui/threaddetails.h new file mode 100644 index 00000000000..a63f4fabc86 --- /dev/null +++ b/gui/threaddetails.h @@ -0,0 +1,25 @@ +#ifndef THREADDETAILS_H +#define THREADDETAILS_H + +#include + +namespace Ui { + class ThreadDetails; +} + +class ThreadDetails : public QWidget +{ + Q_OBJECT + +public: + explicit ThreadDetails(QWidget *parent = nullptr); + ~ThreadDetails() override; + +public slots: + void threadDetailsUpdated(const QString &text); + +private: + Ui::ThreadDetails *mUi; +}; + +#endif // THREADDETAILS_H diff --git a/gui/threaddetails.ui b/gui/threaddetails.ui new file mode 100644 index 00000000000..733505ef363 --- /dev/null +++ b/gui/threaddetails.ui @@ -0,0 +1,24 @@ + + + ThreadDetails + + + + 0 + 0 + 400 + 300 + + + + Thread Details + + + + + + + + + + diff --git a/gui/threadhandler.cpp b/gui/threadhandler.cpp index ad4d0e2a61d..557401da451 100644 --- a/gui/threadhandler.cpp +++ b/gui/threadhandler.cpp @@ -146,11 +146,15 @@ void ThreadHandler::createThreads(const int count) removeThreads(); //Create new threads for (int i = mThreads.size(); i < count; i++) { - mThreads << new CheckThread(mResults); + mThreads << new CheckThread(mResults, i + 1); connect(mThreads.last(), &CheckThread::done, this, &ThreadHandler::threadDone, Qt::QueuedConnection); - connect(mThreads.last(), &CheckThread::fileChecked, - &mResults, &ThreadResult::fileChecked, Qt::QueuedConnection); + connect(mThreads.last(), &CheckThread::finishCheck, + &mResults, &ThreadResult::finishCheck, Qt::QueuedConnection); + connect(mThreads.last(), &CheckThread::startCheck, + this, &ThreadHandler::startCheck, Qt::QueuedConnection); + connect(mThreads.last(), &CheckThread::finishCheck, + this, &ThreadHandler::finishCheck, Qt::QueuedConnection); } } @@ -164,8 +168,12 @@ void ThreadHandler::removeThreads() } disconnect(thread, &CheckThread::done, this, &ThreadHandler::threadDone); - disconnect(thread, &CheckThread::fileChecked, - &mResults, &ThreadResult::fileChecked); + disconnect(thread, &CheckThread::finishCheck, + &mResults, &ThreadResult::finishCheck); + disconnect(mThreads.last(), &CheckThread::startCheck, + this, &ThreadHandler::startCheck); + disconnect(mThreads.last(), &CheckThread::finishCheck, + this, &ThreadHandler::finishCheck); delete thread; } @@ -317,3 +325,37 @@ void ThreadHandler::setCheckStartTime(QDateTime checkStartTime) { mCheckStartTime = std::move(checkStartTime); } + +// cppcheck-suppress passedByValueCallback +// NOLINTNEXTLINE(performance-unnecessary-value-param) +void ThreadHandler::startCheck(CheckThread::Details details) +{ + mThreadDetails[details.threadIndex] = details; + emitThreadDetailsUpdated(); +} + +// NOLINTNEXTLINE(performance-unnecessary-value-param) +void ThreadHandler::finishCheck(CheckThread::Details details) +{ + mThreadDetails.erase(details.threadIndex); + emitThreadDetailsUpdated(); +} + +QString ThreadHandler::buildThreadDetailsText() const +{ + QString result; + + for (const auto &details : mThreadDetails) { + result += QString("Thread %1 (%2): %3\n") + .arg(details.second.threadIndex) + .arg(details.second.startTime.toString(Qt::TextDate)) + .arg(details.second.file); + } + + return result; +} + +void ThreadHandler::emitThreadDetailsUpdated() +{ + emit threadDetailsUpdated(buildThreadDetailsText()); +} diff --git a/gui/threadhandler.h b/gui/threadhandler.h index cc578bd5643..007117beb4d 100644 --- a/gui/threadhandler.h +++ b/gui/threadhandler.h @@ -178,6 +178,11 @@ class ThreadHandler : public QObject { */ void setCheckStartTime(QDateTime checkStartTime); + /** + * @brief Emit the threadDetailsUpdated signal + */ + void emitThreadDetailsUpdated(); + signals: /** * @brief Signal that all threads are done @@ -191,6 +196,13 @@ class ThreadHandler : public QObject { // NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name) - caused by generated MOC code void debugError(const ErrorItem &item); + /** + * @brief Emitted when thread details are updated. + * + * @param text Text for the ThreadDetails widget to display + */ + void threadDetailsUpdated(const QString &text); + public slots: /** @@ -198,6 +210,19 @@ public slots: * */ void stop(); + + /** + * @brief Slot threads use to signal this class that it started checking a file + * @param details Details about what file is being checked and by what thread + */ + void startCheck(CheckThread::Details details); + + /** + * @brief Slot threads use to signal this class that it finish checking a file + * @param details Details about what file finished being checked and by what thread + */ + void finishCheck(CheckThread::Details details); + protected slots: /** * @brief Slot that a single thread is done @@ -285,6 +310,12 @@ protected slots: Settings mCheckSettings; std::shared_ptr mCheckSuppressions; /// @} + + /** + * @bried Details about currently running threads + */ + std::map mThreadDetails; + private: /** @@ -292,6 +323,8 @@ protected slots: * included headers. Used by GetReCheckFiles() */ bool needsReCheck(const QString &filename, std::set &modified, std::set &unmodified) const; + + QString buildThreadDetailsText() const; }; /// @} #endif // THREADHANDLER_H diff --git a/gui/threadresult.cpp b/gui/threadresult.cpp index ea858c92e4a..06399ccb528 100644 --- a/gui/threadresult.cpp +++ b/gui/threadresult.cpp @@ -34,11 +34,12 @@ void ThreadResult::reportOut(const std::string &outmsg, Color /*c*/) emit log(QString::fromStdString(outmsg)); } -void ThreadResult::fileChecked(const QString &file) +// NOLINTNEXTLINE(performance-unnecessary-value-param) +void ThreadResult::finishCheck(CheckThread::Details details) { std::lock_guard locker(mutex); - mProgress += QFile(file).size(); + mProgress += QFile(details.file).size(); mFilesChecked++; if (mMaxProgress > 0) { diff --git a/gui/threadresult.h b/gui/threadresult.h index dc7b5c0372a..e49d1557e9c 100644 --- a/gui/threadresult.h +++ b/gui/threadresult.h @@ -23,6 +23,7 @@ #include "color.h" #include "errorlogger.h" #include "filesettings.h" +#include "checkthread.h" #include #include @@ -85,12 +86,12 @@ class ThreadResult : public QObject, public ErrorLogger { } public slots: - /** - * @brief Slot threads use to signal this class that a specific file is checked - * @param file File that is checked + * @brief Slot threads use to signal this class that it finish checking a file + * @param details Details about what file finished being checked and by what thread */ - void fileChecked(const QString &file); + void finishCheck(CheckThread::Details details); + signals: /** * @brief Progress signal