/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2023 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 "config.h" #include "cppcheck.h" #include "cppcheckexecutor.h" #include "errorlogger.h" #include "importproject.h" #include "settings.h" #include "timer.h" #include #include #include #include #include #include #include #include #include #include #include #include enum class Color; ThreadExecutor::ThreadExecutor(const std::map &files, const Settings &settings, Suppressions &suppressions, ErrorLogger &errorLogger, CppCheck::ExecuteCmdFn executeCommand) : Executor(files, settings, suppressions, errorLogger) , mExecuteCommand(std::move(executeCommand)) { assert(mSettings.jobs > 1); } class SyncLogForwarder : public ErrorLogger { public: explicit SyncLogForwarder(ThreadExecutor &threadExecutor, ErrorLogger &errorLogger) : mThreadExecutor(threadExecutor), mErrorLogger(errorLogger) {} void reportOut(const std::string &outmsg, Color c) override { std::lock_guard lg(mReportSync); mErrorLogger.reportOut(outmsg, c); } void reportErr(const ErrorMessage &msg) override { if (!mThreadExecutor.hasToLog(msg)) return; std::lock_guard lg(mReportSync); mErrorLogger.reportErr(msg); } void reportStatus(std::size_t fileindex, std::size_t filecount, std::size_t sizedone, std::size_t sizetotal) { std::lock_guard lg(mReportSync); mThreadExecutor.reportStatus(fileindex, filecount, sizedone, sizetotal); } private: std::mutex mReportSync; ThreadExecutor &mThreadExecutor; ErrorLogger &mErrorLogger; }; class ThreadData { public: ThreadData(ThreadExecutor &threadExecutor, ErrorLogger &errorLogger, const Settings &settings, const std::map &files, const std::list &fileSettings, CppCheck::ExecuteCmdFn executeCommand) : mFiles(files), mFileSettings(fileSettings), mSettings(settings), mExecuteCommand(std::move(executeCommand)), logForwarder(threadExecutor, errorLogger) { mItNextFile = mFiles.begin(); mItNextFileSettings = mFileSettings.begin(); mTotalFiles = mFiles.size() + mFileSettings.size(); mTotalFileSize = std::accumulate(mFiles.cbegin(), mFiles.cend(), std::size_t(0), [](std::size_t v, const std::pair& p) { return v + p.second; }); } bool next(const std::string *&file, const ImportProject::FileSettings *&fs, std::size_t &fileSize) { std::lock_guard l(mFileSync); if (mItNextFile != mFiles.end()) { file = &mItNextFile->first; fs = nullptr; fileSize = mItNextFile->second; ++mItNextFile; return true; } if (mItNextFileSettings != mFileSettings.end()) { file = nullptr; fs = &(*mItNextFileSettings); fileSize = 0; ++mItNextFileSettings; return true; } return false; } unsigned int check(ErrorLogger &errorLogger, const std::string *file, const ImportProject::FileSettings *fs) const { CppCheck fileChecker(errorLogger, false, mExecuteCommand); fileChecker.settings() = mSettings; // this is a copy unsigned int result; if (fs) { // file settings.. result = fileChecker.check(*fs); if (fileChecker.settings().clangTidy) fileChecker.analyseClangTidy(*fs); } else { // Read file from a file result = fileChecker.check(*file); // TODO: call analyseClangTidy()? } return result; } void status(std::size_t fileSize) { std::lock_guard l(mFileSync); mProcessedSize += fileSize; mProcessedFiles++; if (!mSettings.quiet) logForwarder.reportStatus(mProcessedFiles, mTotalFiles, mProcessedSize, mTotalFileSize); } private: const std::map &mFiles; std::map::const_iterator mItNextFile; const std::list &mFileSettings; std::list::const_iterator mItNextFileSettings; std::size_t mProcessedFiles{}; std::size_t mTotalFiles{}; std::size_t mProcessedSize{}; std::size_t mTotalFileSize{}; std::mutex mFileSync; const Settings &mSettings; CppCheck::ExecuteCmdFn mExecuteCommand; public: SyncLogForwarder logForwarder; }; static unsigned int STDCALL threadProc(ThreadData *data) { unsigned int result = 0; const std::string *file; const ImportProject::FileSettings *fs; std::size_t fileSize; while (data->next(file, fs, fileSize)) { result += data->check(data->logForwarder, file, fs); data->status(fileSize); } return result; } unsigned int ThreadExecutor::check() { std::vector> threadFutures; threadFutures.reserve(mSettings.jobs); ThreadData data(*this, mErrorLogger, mSettings, mFiles, mSettings.project.fileSettings, mExecuteCommand); for (unsigned int i = 0; i < mSettings.jobs; ++i) { try { threadFutures.emplace_back(std::async(std::launch::async, &threadProc, &data)); } catch (const std::system_error &e) { std::cerr << "#### ThreadExecutor::check exception :" << e.what() << std::endl; exit(EXIT_FAILURE); } } unsigned int result = std::accumulate(threadFutures.begin(), threadFutures.end(), 0U, [](unsigned int v, std::future& f) { return v + f.get(); }); if (mSettings.showtime == SHOWTIME_MODES::SHOWTIME_SUMMARY || mSettings.showtime == SHOWTIME_MODES::SHOWTIME_TOP5_SUMMARY) CppCheck::printTimerResults(mSettings.showtime); return result; }