|
Cppcheck
|
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
1.7.6.1