📄 checkmemoryleak.cpp
字号:
/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2009 Daniel Marjamäki, Reijo Tomperi, Nicolas Le Cam, * Leandro Penz, Kimmo Varis, Vesa Pikki * * 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 <http://www.gnu.org/licenses/ */#include "checkmemoryleak.h"#include <algorithm>#include <cstring>#include <iostream>#include <sstream>//---------------------------------------------------------------------------CheckMemoryLeakClass::CheckMemoryLeakClass(const Tokenizer *tokenizer, const Settings &settings, ErrorLogger *errorLogger) : _settings(settings){ _tokenizer = tokenizer; _errorLogger = errorLogger;}CheckMemoryLeakClass::~CheckMemoryLeakClass(){}bool CheckMemoryLeakClass::isclass(const Token *tok){ if (tok->isStandardType()) return false; std::ostringstream pattern; pattern << "struct " << tok->str(); if (Token::findmatch(_tokenizer->tokens(), pattern.str().c_str())) return false; return true;}//---------------------------------------------------------------------------CheckMemoryLeakClass::AllocType CheckMemoryLeakClass::GetAllocationType(const Token *tok2){ // What we may have... // * var = (char *)malloc(10); // * var = new char[10]; // * var = strdup("hello"); if (tok2 && tok2->str() == "(") { while (tok2 && tok2->str() != ")") tok2 = tok2->next(); tok2 = tok2 ? tok2->next() : NULL; } if (! tok2) return No; if (! tok2->isName()) return No; // Does tok2 point on "malloc", "strdup" or "kmalloc".. const char *mallocfunc[] = {"malloc", "calloc", "strdup", "kmalloc", "kzalloc", "kcalloc", 0 }; for (unsigned int i = 0; mallocfunc[i]; i++) { if (tok2->str() == mallocfunc[i]) return Malloc; } // Does tok2 point on "g_malloc", "g_strdup", .. const char *gmallocfunc[] = {"g_new", "g_new0", "g_try_new", "g_try_new0", "g_malloc", "g_malloc0", "g_try_malloc", "g_try_malloc0", "g_strdup", "g_strndup", 0 }; for (unsigned int i = 0; gmallocfunc[i]; i++) { if (tok2->str() == gmallocfunc[i]) return gMalloc; } if (Token::Match(tok2, "new %type% [;(]")) return New; if (Token::Match(tok2, "new %type% [")) return NewArray; if (Token::Match(tok2, "fopen (")) return FOPEN; if (Token::Match(tok2, "popen (")) return POPEN; // Userdefined allocation function.. std::list<AllocFunc>::const_iterator it = _listAllocFunc.begin(); while (it != _listAllocFunc.end()) { if (tok2->str() == it->funcname) return it->alloctype; ++it; } return No;}CheckMemoryLeakClass::AllocType CheckMemoryLeakClass::GetReallocationType(const Token *tok2){ // What we may have... // * var = (char *)realloc(..; if (tok2 && tok2->str() == "(") { while (tok2 && tok2->str() != ")") tok2 = tok2->next(); tok2 = tok2 ? tok2->next() : NULL; } if (! tok2) return No; if (Token::Match(tok2, "realloc")) return Malloc; // GTK memory reallocation.. if (Token::Match(tok2, "g_realloc|g_try_realloc|g_renew|g_try_renew")) return gMalloc; return No;}CheckMemoryLeakClass::AllocType CheckMemoryLeakClass::GetDeallocationType(const Token *tok, const char *varnames[]){ int i = 0; std::string names; while (varnames[i]) { if (i > 0) names += " . "; names += varnames[i]; i++; } if (Token::simpleMatch(tok, std::string("delete " + names + " ;").c_str())) return New; if (Token::simpleMatch(tok, std::string("delete [ ] " + names + " ;").c_str())) return NewArray; if (Token::simpleMatch(tok, std::string("delete ( " + names + " ) ;").c_str())) return New; if (Token::simpleMatch(tok, std::string("delete [ ] ( " + names + " ) ;").c_str())) return NewArray; if (Token::simpleMatch(tok, std::string("free ( " + names + " ) ;").c_str()) || Token::simpleMatch(tok, std::string("kfree ( " + names + " ) ;").c_str())) { return Malloc; } if (Token::simpleMatch(tok, std::string("g_free ( " + names + " ) ;").c_str())) return gMalloc; if (Token::simpleMatch(tok, std::string("fclose ( " + names + " )").c_str())) return FOPEN; if (Token::simpleMatch(tok, std::string("pclose ( " + names + " )").c_str())) return POPEN; return No;}//--------------------------------------------------------------------------const char * CheckMemoryLeakClass::call_func(const Token *tok, std::list<const Token *> callstack, const char *varnames[], AllocType &alloctype, AllocType &dealloctype, bool &all, unsigned int sz){ // Keywords that are not function calls.. if (Token::Match(tok, "if|for|while|return|switch")) return 0; // String functions that are not allocating nor deallocating memory.. if (Token::Match(tok, "strcpy|strncpy|strcat|strncat|strcmp|strncmp|strcasecmp|stricmp|sprintf|strchr|strrchr|strstr")) return 0; // Memory functions that are not allocating nor deallocating memory.. if (Token::Match(tok, "memset|memcpy|memmove|memchr")) return 0; // I/O functions that are not allocating nor deallocating memory.. if (Token::Match(tok, "fgets|fgetc|fputs|fputc|printf")) return 0; // Convert functions that are not allocating nor deallocating memory.. if (Token::Match(tok, "atoi|atof|atol|strtol|strtoul|strtod")) return 0; // This is not an unknown function neither if (tok->str() == "delete") return 0; if (GetAllocationType(tok) != No || GetReallocationType(tok) != No || GetDeallocationType(tok, varnames) != No) return 0; if (callstack.size() > 2) return "dealloc_"; const std::string funcname(tok->str()); for (std::list<const Token *>::const_iterator it = callstack.begin(); it != callstack.end(); ++it) { if ((*it)->str() == funcname) return "recursive"; } callstack.push_back(tok); int par = 1; int parlevel = 0; std::string pattern = "[,()] "; for (int i = 0; varnames[i]; i++) { if (i > 0) pattern += " . "; pattern += varnames[i]; } pattern += " [,()]"; for (; tok; tok = tok->next()) { if (tok->str() == "(") ++parlevel; else if (tok->str() == ")") { --parlevel; if (parlevel < 1) { return _settings._showAll ? 0 : "callfunc"; } } if (parlevel == 1) { if (tok->str() == ",") ++par; if (Token::Match(tok, pattern.c_str())) { const Token *ftok = _tokenizer->GetFunctionTokenByName(funcname.c_str()); const char *parname = Tokenizer::getParameterName(ftok, par); if (! parname) return "recursive"; // Check if the function deallocates the variable.. while (ftok && (ftok->str() != "{")) ftok = ftok->next(); Token *func = getcode(ftok->tokAt(1), callstack, parname, alloctype, dealloctype, false, all, sz); simplifycode(func, all); const Token *func_ = func; while (func_ && func_->str() == ";") func_ = func_->next(); const char *ret = 0; // TODO : "goto" isn't handled well if (Token::findmatch(func_, "dealloc")) ret = "dealloc"; else if (Token::findmatch(func_, "use")) ret = "use"; else if (Token::findmatch(func_, "&use")) ret = "&use"; Tokenizer::deleteTokens(func); return ret; } } } return NULL;}//--------------------------------------------------------------------------void CheckMemoryLeakClass::MemoryLeak(const Token *tok, const char varname[], AllocType alloctype, bool all){ if (alloctype == CheckMemoryLeakClass::FOPEN || alloctype == CheckMemoryLeakClass::POPEN) _errorLogger->resourceLeak(_tokenizer, tok, varname); else if (all) _errorLogger->memleakall(_tokenizer, tok, varname); else _errorLogger->memleak(_tokenizer, tok, varname);}//---------------------------------------------------------------------------bool CheckMemoryLeakClass::MatchFunctionsThatReturnArg(const Token *tok, const std::string &varname){ return Token::Match(tok, std::string("; " + varname + " = strcat|memcpy|memmove|strcpy ( " + varname + " ,").c_str());}bool CheckMemoryLeakClass::notvar(const Token *tok, const char *varnames[], bool endpar){ std::string varname; for (int i = 0; varnames[i]; i++) { if (i > 0) varname += " . "; varname += varnames[i]; } const std::string end(endpar ? " )" : " [;)&|]"); return bool(Token::Match(tok, std::string("! " + varname + end).c_str()) || Token::simpleMatch(tok, std::string("! ( " + varname + " )" + end).c_str()) || Token::Match(tok, std::string("0 == " + varname + end).c_str()) || Token::simpleMatch(tok, std::string(varname + " == 0" + end).c_str()));}Token *CheckMemoryLeakClass::getcode(const Token *tok, std::list<const Token *> callstack, const char varname[], AllocType &alloctype, AllocType &dealloctype, bool classmember, bool &all, unsigned int sz){ const char *varnames[2]; varnames[0] = varname; varnames[1] = 0; std::string varnameStr = varname; Token *rethead = 0, *rettail = 0;#define addtoken(_str) \ { \ if (rettail) \ { \ rettail->insertToken(_str); \ rettail = rettail->next(); \ } \ else \ { \ rethead = new Token; \ rettail = rethead; \ rettail->str(_str); \ } \ \ rettail->linenr( tok->linenr() ); \ rettail->fileIndex( tok->fileIndex() ); \ } // The first token should be ";" addtoken(";"); bool isloop = false; int indentlevel = 0; int parlevel = 0; for (; tok; tok = tok->next()) { if (tok->str() == "{") { addtoken("{"); ++indentlevel; } else if (tok->str() == "}") { addtoken("}"); if (indentlevel <= 0) break; --indentlevel; } if (tok->str() == "(") ++parlevel; else if (tok->str() == ")") --parlevel; isloop &= (parlevel > 0); if (parlevel == 0 && tok->str() == ";") addtoken(";"); if (Token::Match(tok->previous(), std::string("[(;{}] " + varnameStr + " =").c_str())) { AllocType alloc = GetAllocationType(tok->tokAt(2)); bool realloc = false; if (sz > 1 && Token::Match(tok->tokAt(2), "malloc ( %num% )") && (std::atoi(tok->strAt(4)) % sz) != 0) { _errorLogger->mismatchSize(_tokenizer, tok->tokAt(4), tok->strAt(4)); } if (alloc == No)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -