Cppcheck
threadexecutor.cpp
Go to the documentation of this file.
00001 /*
00002  * Cppcheck - A tool for static C/C++ code analysis
00003  * Copyright (C) 2007-2013 Daniel Marjamäki and Cppcheck team.
00004  *
00005  * This program is free software: you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation, either version 3 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00017  */
00018 
00019 #include "cppcheckexecutor.h"
00020 #include "threadexecutor.h"
00021 #include "cppcheck.h"
00022 #ifdef THREADING_MODEL_FORK
00023 #include <algorithm>
00024 #include <iostream>
00025 #include <sys/select.h>
00026 #include <sys/wait.h>
00027 #include <unistd.h>
00028 #include <fcntl.h>
00029 #include <cstdlib>
00030 #include <cstdio>
00031 #include <errno.h>
00032 #include <time.h>
00033 #include <cstring>
00034 #include <sstream>
00035 #endif
00036 #ifdef THREADING_MODEL_WIN
00037 #include <process.h>
00038 #include <windows.h>
00039 #include <algorithm>
00040 #include <cstring>
00041 #include <errno.h>
00042 #endif
00043 
00044 // required for FD_ZERO
00045 using std::memset;
00046 
00047 ThreadExecutor::ThreadExecutor(const std::map<std::string, std::size_t> &files, Settings &settings, ErrorLogger &errorLogger)
00048     : _files(files), _settings(settings), _errorLogger(errorLogger), _fileCount(0)
00049 {
00050 #if defined(THREADING_MODEL_FORK)
00051     _wpipe = 0;
00052 #elif defined(THREADING_MODEL_WIN)
00053     _processedFiles = 0;
00054     _totalFiles = 0;
00055     _processedSize = 0;
00056     _totalFileSize = 0;
00057 #endif
00058 }
00059 
00060 ThreadExecutor::~ThreadExecutor()
00061 {
00062     //dtor
00063 }
00064 
00065 
00066 ///////////////////////////////////////////////////////////////////////////////
00067 ////// This code is for platforms that support fork() only ////////////////////
00068 ///////////////////////////////////////////////////////////////////////////////
00069 
00070 #if defined(THREADING_MODEL_FORK)
00071 
00072 void ThreadExecutor::addFileContent(const std::string &path, const std::string &content)
00073 {
00074     _fileContents[ path ] = content;
00075 }
00076 
00077 int ThreadExecutor::handleRead(int rpipe, unsigned int &result)
00078 {
00079     char type = 0;
00080     if (read(rpipe, &type, 1) <= 0) {
00081         if (errno == EAGAIN)
00082             return 0;
00083 
00084         return -1;
00085     }
00086 
00087     if (type != REPORT_OUT && type != REPORT_ERROR && type != REPORT_INFO && type != CHILD_END) {
00088         std::cerr << "#### You found a bug from cppcheck.\nThreadExecutor::handleRead error, type was:" << type << std::endl;
00089         std::exit(0);
00090     }
00091 
00092     unsigned int len = 0;
00093     if (read(rpipe, &len, sizeof(len)) <= 0) {
00094         std::cerr << "#### You found a bug from cppcheck.\nThreadExecutor::handleRead error, type was:" << type << std::endl;
00095         std::exit(0);
00096     }
00097 
00098     char *buf = new char[len];
00099     if (read(rpipe, buf, len) <= 0) {
00100         std::cerr << "#### You found a bug from cppcheck.\nThreadExecutor::handleRead error, type was:" << type << std::endl;
00101         std::exit(0);
00102     }
00103 
00104     if (type == REPORT_OUT) {
00105         _errorLogger.reportOut(buf);
00106     } else if (type == REPORT_ERROR || type == REPORT_INFO) {
00107         ErrorLogger::ErrorMessage msg;
00108         msg.deserialize(buf);
00109 
00110         std::string file;
00111         unsigned int line(0);
00112         if (!msg._callStack.empty()) {
00113             file = msg._callStack.back().getfile(false);
00114             line = msg._callStack.back().line;
00115         }
00116 
00117         if (!_settings.nomsg.isSuppressed(msg._id, file, line)) {
00118             // Alert only about unique errors
00119             std::string errmsg = msg.toString(_settings._verbose);
00120             if (std::find(_errorList.begin(), _errorList.end(), errmsg) == _errorList.end()) {
00121                 _errorList.push_back(errmsg);
00122                 if (type == REPORT_ERROR)
00123                     _errorLogger.reportErr(msg);
00124                 else
00125                     _errorLogger.reportInfo(msg);
00126             }
00127         }
00128     } else if (type == CHILD_END) {
00129         std::istringstream iss(buf);
00130         unsigned int fileResult = 0;
00131         iss >> fileResult;
00132         result += fileResult;
00133         delete [] buf;
00134         return -1;
00135     }
00136 
00137     delete [] buf;
00138     return 1;
00139 }
00140 
00141 unsigned int ThreadExecutor::check()
00142 {
00143     _fileCount = 0;
00144     unsigned int result = 0;
00145 
00146     std::size_t totalfilesize = 0;
00147     for (std::map<std::string, std::size_t>::const_iterator i = _files.begin(); i != _files.end(); ++i) {
00148         totalfilesize += i->second;
00149     }
00150 
00151     std::list<int> rpipes;
00152     std::map<pid_t, std::string> childFile;
00153     std::map<int, std::string> pipeFile;
00154     std::size_t processedsize = 0;
00155     std::map<std::string, std::size_t>::const_iterator i = _files.begin();
00156     for (;;) {
00157         // Start a new child
00158         if (i != _files.end() && rpipes.size() < _settings._jobs) {
00159             int pipes[2];
00160             if (pipe(pipes) == -1) {
00161                 std::cerr << "pipe() failed: "<< std::strerror(errno) << std::endl;
00162                 std::exit(EXIT_FAILURE);
00163             }
00164 
00165             int flags = 0;
00166             if ((flags = fcntl(pipes[0], F_GETFL, 0)) < 0) {
00167                 std::cerr << "fcntl(F_GETFL) failed: "<< std::strerror(errno) << std::endl;
00168                 std::exit(EXIT_FAILURE);
00169             }
00170 
00171             if (fcntl(pipes[0], F_SETFL, flags | O_NONBLOCK) < 0) {
00172                 std::cerr << "fcntl(F_SETFL) failed: "<< std::strerror(errno) << std::endl;
00173                 std::exit(EXIT_FAILURE);
00174             }
00175 
00176             pid_t pid = fork();
00177             if (pid < 0) {
00178                 // Error
00179                 std::cerr << "Failed to create child process: "<< std::strerror(errno) << std::endl;
00180                 std::exit(EXIT_FAILURE);
00181             } else if (pid == 0) {
00182                 close(pipes[0]);
00183                 _wpipe = pipes[1];
00184 
00185                 CppCheck fileChecker(*this, false);
00186                 fileChecker.settings() = _settings;
00187                 unsigned int resultOfCheck = 0;
00188 
00189                 if (!_fileContents.empty() && _fileContents.find(i->first) != _fileContents.end()) {
00190                     // File content was given as a string
00191                     resultOfCheck = fileChecker.check(i->first, _fileContents[ i->first ]);
00192                 } else {
00193                     // Read file from a file
00194                     resultOfCheck = fileChecker.check(i->first);
00195                 }
00196 
00197                 std::ostringstream oss;
00198                 oss << resultOfCheck;
00199                 writeToPipe(CHILD_END, oss.str());
00200                 std::exit(0);
00201             }
00202 
00203             close(pipes[1]);
00204             rpipes.push_back(pipes[0]);
00205             childFile[pid] = i->first;
00206             pipeFile[pipes[0]] = i->first;
00207 
00208             ++i;
00209         } else if (!rpipes.empty()) {
00210             fd_set rfds;
00211             FD_ZERO(&rfds);
00212             for (std::list<int>::const_iterator rp = rpipes.begin(); rp != rpipes.end(); ++rp)
00213                 FD_SET(*rp, &rfds);
00214 
00215             int r = select(*std::max_element(rpipes.begin(), rpipes.end()) + 1, &rfds, NULL, NULL, NULL);
00216 
00217             if (r > 0) {
00218                 std::list<int>::iterator rp = rpipes.begin();
00219                 while (rp != rpipes.end()) {
00220                     if (FD_ISSET(*rp, &rfds)) {
00221                         int readRes = handleRead(*rp, result);
00222                         if (readRes == -1) {
00223                             std::size_t size = 0;
00224                             std::map<int, std::string>::iterator p = pipeFile.find(*rp);
00225                             if (p != pipeFile.end()) {
00226                                 std::string name = p->second;
00227                                 pipeFile.erase(p);
00228                                 std::map<std::string, std::size_t>::const_iterator fs = _files.find(name);
00229                                 if (fs != _files.end()) {
00230                                     size = fs->second;
00231                                 }
00232                             }
00233 
00234                             _fileCount++;
00235                             processedsize += size;
00236                             if (!_settings._errorsOnly)
00237                                 CppCheckExecutor::reportStatus(_fileCount, _files.size(), processedsize, totalfilesize);
00238 
00239                             close(*rp);
00240                             rp = rpipes.erase(rp);
00241                         } else
00242                             ++rp;
00243                     } else
00244                         ++rp;
00245                 }
00246             }
00247 
00248             int stat = 0;
00249             pid_t child = waitpid(0, &stat, WNOHANG);
00250             if (child > 0) {
00251                 std::string childname;
00252                 std::map<pid_t, std::string>::iterator c = childFile.find(child);
00253                 if (c != childFile.end()) {
00254                     childname = c->second;
00255                     childFile.erase(c);
00256                 }
00257 
00258                 if (WIFSIGNALED(stat)) {
00259                     std::ostringstream oss;
00260                     oss << "Internal error: Child process crashed with signal " << WTERMSIG(stat);
00261 
00262                     std::list<ErrorLogger::ErrorMessage::FileLocation> locations;
00263                     locations.push_back(ErrorLogger::ErrorMessage::FileLocation(childname, 0));
00264                     const ErrorLogger::ErrorMessage errmsg(locations,
00265                                                            Severity::error,
00266                                                            oss.str(),
00267                                                            "cppcheckError",
00268                                                            false);
00269 
00270                     if (!_settings.nomsg.isSuppressed(errmsg._id, childname, 0))
00271                         _errorLogger.reportErr(errmsg);
00272                 }
00273             }
00274         } else {
00275             // All done
00276             break;
00277         }
00278     }
00279 
00280 
00281     return result;
00282 }
00283 
00284 void ThreadExecutor::writeToPipe(PipeSignal type, const std::string &data)
00285 {
00286     unsigned int len = static_cast<unsigned int>(data.length() + 1);
00287     char *out = new char[ len + 1 + sizeof(len)];
00288     out[0] = static_cast<char>(type);
00289     std::memcpy(&(out[1]), &len, sizeof(len));
00290     std::memcpy(&(out[1+sizeof(len)]), data.c_str(), len);
00291     if (write(_wpipe, out, len + 1 + sizeof(len)) <= 0) {
00292         delete [] out;
00293         out = 0;
00294         std::cerr << "#### ThreadExecutor::writeToPipe, Failed to write to pipe" << std::endl;
00295         std::exit(0);
00296     }
00297 
00298     delete [] out;
00299 }
00300 
00301 void ThreadExecutor::reportOut(const std::string &outmsg)
00302 {
00303     writeToPipe(REPORT_OUT, outmsg);
00304 }
00305 
00306 void ThreadExecutor::reportErr(const ErrorLogger::ErrorMessage &msg)
00307 {
00308     writeToPipe(REPORT_ERROR, msg.serialize());
00309 }
00310 
00311 void ThreadExecutor::reportInfo(const ErrorLogger::ErrorMessage &msg)
00312 {
00313     writeToPipe(REPORT_INFO, msg.serialize());
00314 }
00315 
00316 #elif defined(THREADING_MODEL_WIN)
00317 
00318 void ThreadExecutor::addFileContent(const std::string &path, const std::string &content)
00319 {
00320     _fileContents[path] = content;
00321 }
00322 
00323 unsigned int ThreadExecutor::check()
00324 {
00325     HANDLE *threadHandles = new HANDLE[_settings._jobs];
00326 
00327     _itNextFile = _files.begin();
00328 
00329     _processedFiles = 0;
00330     _processedSize = 0;
00331     _totalFiles = _files.size();
00332     _totalFileSize = 0;
00333     for (std::map<std::string, std::size_t>::const_iterator i = _files.begin(); i != _files.end(); ++i) {
00334         _totalFileSize += i->second;
00335     }
00336 
00337     InitializeCriticalSection(&_fileSync);
00338     InitializeCriticalSection(&_errorSync);
00339     InitializeCriticalSection(&_reportSync);
00340 
00341     for (unsigned int i = 0; i < _settings._jobs; ++i) {
00342         threadHandles[i] = (HANDLE)_beginthreadex(NULL, 0, threadProc, this, 0, NULL);
00343         if (!threadHandles[i]) {
00344             std::cerr << "#### .\nThreadExecutor::check error, errno :" << errno << std::endl;
00345             exit(EXIT_FAILURE);
00346         }
00347     }
00348 
00349     DWORD waitResult = WaitForMultipleObjects(_settings._jobs, threadHandles, TRUE, INFINITE);
00350     if (waitResult != WAIT_OBJECT_0) {
00351         if (waitResult == WAIT_FAILED) {
00352             std::cerr << "#### .\nThreadExecutor::check wait failed, result: " << waitResult << " error: " << GetLastError() << std::endl;
00353             exit(EXIT_FAILURE);
00354         } else {
00355             std::cerr << "#### .\nThreadExecutor::check wait failed, result: " << waitResult << std::endl;
00356             exit(EXIT_FAILURE);
00357         }
00358     }
00359 
00360     unsigned int result = 0;
00361     for (unsigned int i = 0; i < _settings._jobs; ++i) {
00362         DWORD exitCode;
00363 
00364         if (!GetExitCodeThread(threadHandles[i], &exitCode)) {
00365             std::cerr << "#### .\nThreadExecutor::check get exit code failed, error:" << GetLastError() << std::endl;
00366             exit(EXIT_FAILURE);
00367         }
00368 
00369         result += exitCode;
00370 
00371         if (!CloseHandle(threadHandles[i])) {
00372             std::cerr << "#### .\nThreadExecutor::check close handle failed, error:" << GetLastError() << std::endl;
00373             exit(EXIT_FAILURE);
00374         }
00375     }
00376 
00377     DeleteCriticalSection(&_fileSync);
00378     DeleteCriticalSection(&_errorSync);
00379     DeleteCriticalSection(&_reportSync);
00380 
00381     delete[] threadHandles;
00382 
00383     return result;
00384 }
00385 
00386 unsigned int __stdcall ThreadExecutor::threadProc(void *args)
00387 {
00388     unsigned int result = 0;
00389 
00390     ThreadExecutor *threadExecutor = static_cast<ThreadExecutor*>(args);
00391     std::map<std::string, std::size_t>::const_iterator &it = threadExecutor->_itNextFile;
00392 
00393     // guard static members of CppCheck against concurrent access
00394     EnterCriticalSection(&threadExecutor->_fileSync);
00395 
00396     CppCheck fileChecker(*threadExecutor, false);
00397     fileChecker.settings() = threadExecutor->_settings;
00398 
00399     LeaveCriticalSection(&threadExecutor->_fileSync);
00400 
00401     for (;;) {
00402 
00403         EnterCriticalSection(&threadExecutor->_fileSync);
00404 
00405         if (it == threadExecutor->_files.end()) {
00406             LeaveCriticalSection(&threadExecutor->_fileSync);
00407             return result;
00408 
00409         }
00410         const std::string &file = it->first;
00411         const std::size_t fileSize = it->second;
00412         it++;
00413 
00414         LeaveCriticalSection(&threadExecutor->_fileSync);
00415 
00416         if (!threadExecutor->_fileContents.empty() && threadExecutor->_fileContents.find(file) != threadExecutor->_fileContents.end()) {
00417             // File content was given as a string
00418             result += fileChecker.check(file, threadExecutor->_fileContents[file]);
00419         } else {
00420             // Read file from a file
00421             result += fileChecker.check(file);
00422         }
00423 
00424         EnterCriticalSection(&threadExecutor->_fileSync);
00425 
00426         threadExecutor->_processedSize += fileSize;
00427         threadExecutor->_processedFiles++;
00428         if (!threadExecutor->_settings._errorsOnly) {
00429             EnterCriticalSection(&threadExecutor->_reportSync);
00430             CppCheckExecutor::reportStatus(threadExecutor->_processedFiles, threadExecutor->_totalFiles, threadExecutor->_processedSize, threadExecutor->_totalFileSize);
00431             LeaveCriticalSection(&threadExecutor->_reportSync);
00432         }
00433 
00434         LeaveCriticalSection(&threadExecutor->_fileSync);
00435     };
00436 
00437     return result;
00438 }
00439 
00440 void ThreadExecutor::reportOut(const std::string &outmsg)
00441 {
00442     EnterCriticalSection(&_reportSync);
00443 
00444     _errorLogger.reportOut(outmsg);
00445 
00446     LeaveCriticalSection(&_reportSync);
00447 }
00448 void ThreadExecutor::reportErr(const ErrorLogger::ErrorMessage &msg)
00449 {
00450     report(msg, REPORT_ERROR);
00451 }
00452 
00453 void ThreadExecutor::reportInfo(const ErrorLogger::ErrorMessage &msg)
00454 {
00455     report(msg, REPORT_INFO);
00456 }
00457 
00458 void ThreadExecutor::report(const ErrorLogger::ErrorMessage &msg, MessageType msgType)
00459 {
00460     std::string file;
00461     unsigned int line(0);
00462     if (!msg._callStack.empty()) {
00463         file = msg._callStack.back().getfile(false);
00464         line = msg._callStack.back().line;
00465     }
00466 
00467     if (_settings.nomsg.isSuppressed(msg._id, file, line))
00468         return;
00469 
00470     // Alert only about unique errors
00471     bool reportError = false;
00472     std::string errmsg = msg.toString(_settings._verbose);
00473 
00474     EnterCriticalSection(&_errorSync);
00475     if (std::find(_errorList.begin(), _errorList.end(), errmsg) == _errorList.end()) {
00476         _errorList.push_back(errmsg);
00477         reportError = true;
00478     }
00479     LeaveCriticalSection(&_errorSync);
00480 
00481     if (reportError) {
00482         EnterCriticalSection(&_reportSync);
00483 
00484         switch (msgType) {
00485         case REPORT_ERROR:
00486             _errorLogger.reportErr(msg);
00487             break;
00488         case REPORT_INFO:
00489             _errorLogger.reportInfo(msg);
00490             break;
00491         }
00492 
00493         LeaveCriticalSection(&_reportSync);
00494     }
00495 }
00496 
00497 #else
00498 
00499 void ThreadExecutor::addFileContent(const std::string &/*path*/, const std::string &/*content*/)
00500 {
00501 
00502 }
00503 
00504 unsigned int ThreadExecutor::check()
00505 {
00506     return 0;
00507 }
00508 
00509 void ThreadExecutor::reportOut(const std::string &/*outmsg*/)
00510 {
00511 
00512 }
00513 void ThreadExecutor::reportErr(const ErrorLogger::ErrorMessage &/*msg*/)
00514 {
00515 
00516 }
00517 
00518 void ThreadExecutor::reportInfo(const ErrorLogger::ErrorMessage &/*msg*/)
00519 {
00520 
00521 }
00522 
00523 #endif