1 /*
2 * Cppcheck - A tool for static C/C++ code analysis
3 * Copyright (C) 2007-2021 Cppcheck team.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "threadhandler.h"
20
21 #include <QFileInfo>
22 #include <QDebug>
23 #include <QSettings>
24 #include "common.h"
25 #include "settings.h"
26 #include "checkthread.h"
27 #include "resultsview.h"
28
ThreadHandler(QObject * parent)29 ThreadHandler::ThreadHandler(QObject *parent) :
30 QObject(parent),
31 mScanDuration(0),
32 mRunningThreadCount(0),
33 mAnalyseWholeProgram(false)
34
35 {
36 setThreadCount(1);
37 }
38
~ThreadHandler()39 ThreadHandler::~ThreadHandler()
40 {
41 removeThreads();
42 }
43
clearFiles()44 void ThreadHandler::clearFiles()
45 {
46 mLastFiles.clear();
47 mResults.clearFiles();
48 mAnalyseWholeProgram = false;
49 mAddonsAndTools.clear();
50 mSuppressions.clear();
51 }
52
setFiles(const QStringList & files)53 void ThreadHandler::setFiles(const QStringList &files)
54 {
55 mResults.setFiles(files);
56 mLastFiles = files;
57 }
58
setProject(const ImportProject & prj)59 void ThreadHandler::setProject(const ImportProject &prj)
60 {
61 mResults.setProject(prj);
62 mLastFiles.clear();
63 }
64
setCheckFiles(bool all)65 void ThreadHandler::setCheckFiles(bool all)
66 {
67 if (mRunningThreadCount == 0) {
68 mResults.setFiles(getReCheckFiles(all));
69 }
70 }
71
setCheckFiles(const QStringList & files)72 void ThreadHandler::setCheckFiles(const QStringList& files)
73 {
74 if (mRunningThreadCount == 0) {
75 mResults.setFiles(files);
76 }
77 }
78
check(const Settings & settings)79 void ThreadHandler::check(const Settings &settings)
80 {
81 if (mResults.getFileCount() == 0 || mRunningThreadCount > 0 || settings.jobs == 0) {
82 qDebug() << "Can't start checking if there's no files to check or if check is in progress.";
83 emit done();
84 return;
85 }
86
87 setThreadCount(settings.jobs);
88
89 mRunningThreadCount = mThreads.size();
90
91 if (mResults.getFileCount() < mRunningThreadCount) {
92 mRunningThreadCount = mResults.getFileCount();
93 }
94
95 QStringList addonsAndTools = mAddonsAndTools;
96 for (const std::string& addon: settings.addons) {
97 QString s = QString::fromStdString(addon);
98 if (!addonsAndTools.contains(s))
99 addonsAndTools << s;
100 }
101
102 for (int i = 0; i < mRunningThreadCount; i++) {
103 mThreads[i]->setAddonsAndTools(addonsAndTools);
104 mThreads[i]->setSuppressions(mSuppressions);
105 mThreads[i]->setClangIncludePaths(mClangIncludePaths);
106 mThreads[i]->setDataDir(mDataDir);
107 mThreads[i]->check(settings);
108 }
109
110 // Date and time when checking starts..
111 mCheckStartTime = QDateTime::currentDateTime();
112
113 mAnalyseWholeProgram = true;
114
115 mTime.start();
116 }
117
isChecking() const118 bool ThreadHandler::isChecking() const
119 {
120 return mRunningThreadCount > 0;
121 }
122
setThreadCount(const int count)123 void ThreadHandler::setThreadCount(const int count)
124 {
125 if (mRunningThreadCount > 0 ||
126 count == mThreads.size() ||
127 count <= 0) {
128 return;
129 }
130
131 //Remove unused old threads
132 removeThreads();
133 //Create new threads
134 for (int i = mThreads.size(); i < count; i++) {
135 mThreads << new CheckThread(mResults);
136 connect(mThreads.last(), &CheckThread::done,
137 this, &ThreadHandler::threadDone);
138 connect(mThreads.last(), &CheckThread::fileChecked,
139 &mResults, &ThreadResult::fileChecked);
140 }
141 }
142
143
removeThreads()144 void ThreadHandler::removeThreads()
145 {
146 for (CheckThread* thread : mThreads) {
147 thread->terminate();
148 disconnect(thread, &CheckThread::done,
149 this, &ThreadHandler::threadDone);
150 disconnect(thread, &CheckThread::fileChecked,
151 &mResults, &ThreadResult::fileChecked);
152 delete thread;
153 }
154
155 mThreads.clear();
156 mAnalyseWholeProgram = false;
157 }
158
threadDone()159 void ThreadHandler::threadDone()
160 {
161 if (mRunningThreadCount == 1 && mAnalyseWholeProgram) {
162 mThreads[0]->analyseWholeProgram(mLastFiles);
163 mAnalyseWholeProgram = false;
164 return;
165 }
166
167 mRunningThreadCount--;
168 if (mRunningThreadCount == 0) {
169 emit done();
170
171 mScanDuration = mTime.elapsed();
172
173 // Set date/time used by the recheck
174 if (!mCheckStartTime.isNull()) {
175 mLastCheckTime = mCheckStartTime;
176 mCheckStartTime = QDateTime();
177 }
178 }
179 }
180
stop()181 void ThreadHandler::stop()
182 {
183 mCheckStartTime = QDateTime();
184 mAnalyseWholeProgram = false;
185 for (CheckThread* thread : mThreads) {
186 thread->stop();
187 }
188 }
189
initialize(ResultsView * view)190 void ThreadHandler::initialize(ResultsView *view)
191 {
192 connect(&mResults, &ThreadResult::progress,
193 view, &ResultsView::progress);
194
195 connect(&mResults, &ThreadResult::error,
196 view, &ResultsView::error);
197
198 connect(&mResults, &ThreadResult::log,
199 this, &ThreadHandler::log);
200
201 connect(&mResults, &ThreadResult::debugError,
202 this, &ThreadHandler::debugError);
203
204 connect(&mResults, &ThreadResult::bughuntingReportLine,
205 this, &ThreadHandler::bughuntingReportLine);
206 }
207
loadSettings(const QSettings & settings)208 void ThreadHandler::loadSettings(const QSettings &settings)
209 {
210 setThreadCount(settings.value(SETTINGS_CHECK_THREADS, 1).toInt());
211 }
212
saveSettings(QSettings & settings) const213 void ThreadHandler::saveSettings(QSettings &settings) const
214 {
215 settings.setValue(SETTINGS_CHECK_THREADS, mThreads.size());
216 }
217
hasPreviousFiles() const218 bool ThreadHandler::hasPreviousFiles() const
219 {
220 return !mLastFiles.isEmpty();
221 }
222
getPreviousFilesCount() const223 int ThreadHandler::getPreviousFilesCount() const
224 {
225 return mLastFiles.size();
226 }
227
getPreviousScanDuration() const228 int ThreadHandler::getPreviousScanDuration() const
229 {
230 return mScanDuration;
231 }
232
getReCheckFiles(bool all) const233 QStringList ThreadHandler::getReCheckFiles(bool all) const
234 {
235 if (mLastCheckTime.isNull() || all)
236 return mLastFiles;
237
238 std::set<QString> modified;
239 std::set<QString> unmodified;
240
241 QStringList files;
242 for (int i = 0; i < mLastFiles.size(); ++i) {
243 if (needsReCheck(mLastFiles[i], modified, unmodified))
244 files.push_back(mLastFiles[i]);
245 }
246 return files;
247 }
248
needsReCheck(const QString & filename,std::set<QString> & modified,std::set<QString> & unmodified) const249 bool ThreadHandler::needsReCheck(const QString &filename, std::set<QString> &modified, std::set<QString> &unmodified) const
250 {
251 if (modified.find(filename) != modified.end())
252 return true;
253
254 if (unmodified.find(filename) != unmodified.end())
255 return false;
256
257 if (QFileInfo(filename).lastModified() > mLastCheckTime) {
258 return true;
259 }
260
261 // Parse included files recursively
262 QFile f(filename);
263 if (!f.open(QIODevice::ReadOnly | QIODevice::Text))
264 return false;
265
266 // prevent recursion..
267 unmodified.insert(filename);
268
269 QTextStream in(&f);
270 while (!in.atEnd()) {
271 QString line = in.readLine();
272 if (line.startsWith("#include \"")) {
273 line.remove(0,10);
274 int i = line.indexOf("\"");
275 if (i > 0) {
276 line.remove(i,line.length());
277 line = QFileInfo(filename).absolutePath() + "/" + line;
278 if (needsReCheck(line, modified, unmodified)) {
279 modified.insert(line);
280 return true;
281 }
282 }
283 }
284 }
285
286 return false;
287 }
288
getCheckStartTime() const289 QDateTime ThreadHandler::getCheckStartTime() const
290 {
291 return mCheckStartTime;
292 }
293
setCheckStartTime(QDateTime checkStartTime)294 void ThreadHandler::setCheckStartTime(QDateTime checkStartTime)
295 {
296 mCheckStartTime = std::move(checkStartTime);
297 }
298