/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * 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 . */ #include "threadexecutor.h" #include "color.h" #include "cppcheck.h" #include "cppcheckexecutor.h" #include "errorlogger.h" #include "importproject.h" #include "settings.h" #include "suppressions.h" #include #include #include #include #include #include #include #include #include #include #include ThreadExecutor::ThreadExecutor(const std::map &files, Settings &settings, ErrorLogger &errorLogger) : Executor(files, settings, errorLogger) {} ThreadExecutor::~ThreadExecutor() {} class ThreadExecutor::SyncLogForwarder : public ErrorLogger { public: explicit SyncLogForwarder(ThreadExecutor &threadExecutor) : mThreadExecutor(threadExecutor), mProcessedFiles(0), mTotalFiles(0), mProcessedSize(0), mTotalFileSize(0) { mItNextFile = mThreadExecutor.mFiles.begin(); mItNextFileSettings = mThreadExecutor.mSettings.project.fileSettings.begin(); mTotalFiles = mThreadExecutor.mFiles.size() + mThreadExecutor.mSettings.project.fileSettings.size(); for (std::map::const_iterator i = mThreadExecutor.mFiles.begin(); i != mThreadExecutor.mFiles.end(); ++i) { mTotalFileSize += i->second; } } void reportOut(const std::string &outmsg, Color c) override { std::lock_guard lg(mReportSync); mThreadExecutor.mErrorLogger.reportOut(outmsg, c); } void reportErr(const ErrorMessage &msg) override { report(msg, MessageType::REPORT_ERROR); } void reportInfo(const ErrorMessage &msg) override { report(msg, MessageType::REPORT_INFO); } ThreadExecutor &mThreadExecutor; std::map::const_iterator mItNextFile; std::list::const_iterator mItNextFileSettings; std::size_t mProcessedFiles; std::size_t mTotalFiles; std::size_t mProcessedSize; std::size_t mTotalFileSize; std::mutex mFileSync; std::mutex mErrorSync; std::mutex mReportSync; private: enum class MessageType {REPORT_ERROR, REPORT_INFO}; void report(const ErrorMessage &msg, MessageType msgType) { if (mThreadExecutor.mSettings.nomsg.isSuppressed(msg.toSuppressionsErrorMessage())) return; // Alert only about unique errors bool reportError = false; const std::string errmsg = msg.toString(mThreadExecutor.mSettings.verbose); { std::lock_guard lg(mErrorSync); if (std::find(mThreadExecutor.mErrorList.begin(), mThreadExecutor.mErrorList.end(), errmsg) == mThreadExecutor.mErrorList.end()) { mThreadExecutor.mErrorList.emplace_back(errmsg); reportError = true; } } if (reportError) { std::lock_guard lg(mReportSync); switch (msgType) { case MessageType::REPORT_ERROR: mThreadExecutor.mErrorLogger.reportErr(msg); break; case MessageType::REPORT_INFO: mThreadExecutor.mErrorLogger.reportInfo(msg); break; } } } }; unsigned int ThreadExecutor::check() { std::vector> threadFutures; threadFutures.reserve(mSettings.jobs); SyncLogForwarder logforwarder(*this); for (unsigned int i = 0; i < mSettings.jobs; ++i) { try { threadFutures.emplace_back(std::async(std::launch::async, threadProc, &logforwarder)); } catch (const std::system_error &e) { std::cerr << "#### ThreadExecutor::check exception :" << e.what() << std::endl; exit(EXIT_FAILURE); } } return std::accumulate(threadFutures.begin(), threadFutures.end(), 0U, [](unsigned int v, std::future& f) { return v + f.get(); }); } unsigned int STDCALL ThreadExecutor::threadProc(SyncLogForwarder* logForwarder) { unsigned int result = 0; std::map::const_iterator &itFile = logForwarder->mItNextFile; std::list::const_iterator &itFileSettings = logForwarder->mItNextFileSettings; // guard static members of CppCheck against concurrent access logForwarder->mFileSync.lock(); for (;;) { if (itFile == logForwarder->mThreadExecutor.mFiles.end() && itFileSettings == logForwarder->mThreadExecutor.mSettings.project.fileSettings.end()) { logForwarder->mFileSync.unlock(); break; } CppCheck fileChecker(*logForwarder, false, CppCheckExecutor::executeCommand); fileChecker.settings() = logForwarder->mThreadExecutor.mSettings; std::size_t fileSize = 0; if (itFile != logForwarder->mThreadExecutor.mFiles.end()) { const std::string &file = itFile->first; fileSize = itFile->second; ++itFile; logForwarder->mFileSync.unlock(); // Read file from a file result += fileChecker.check(file); } else { // file settings.. const ImportProject::FileSettings &fs = *itFileSettings; ++itFileSettings; logForwarder->mFileSync.unlock(); result += fileChecker.check(fs); if (logForwarder->mThreadExecutor.mSettings.clangTidy) fileChecker.analyseClangTidy(fs); } logForwarder->mFileSync.lock(); logForwarder->mProcessedSize += fileSize; logForwarder->mProcessedFiles++; if (!logForwarder->mThreadExecutor.mSettings.quiet) { std::lock_guard lg(logForwarder->mReportSync); CppCheckExecutor::reportStatus(logForwarder->mProcessedFiles, logForwarder->mTotalFiles, logForwarder->mProcessedSize, logForwarder->mTotalFileSize); } } return result; }