From 86757de4d5ffc0118e6e10b358e199d847949a9a Mon Sep 17 00:00:00 2001 From: Paul Fultz II Date: Sun, 30 Apr 2023 13:39:05 -0500 Subject: [PATCH] ValueFlow: Add pass runner to check timeout and report time (#4952) --- Makefile | 2 +- lib/cppcheck.cpp | 6 +- lib/tokenize.cpp | 4 +- lib/valueflow.cpp | 381 +++++++++++++++++++++++++++++----------------- lib/valueflow.h | 7 +- 5 files changed, 256 insertions(+), 144 deletions(-) diff --git a/Makefile b/Makefile index 65ca5504e..0590c4910 100644 --- a/Makefile +++ b/Makefile @@ -628,7 +628,7 @@ $(libcppdir)/tokenlist.o: lib/tokenlist.cpp externals/simplecpp/simplecpp.h lib/ $(libcppdir)/utils.o: lib/utils.cpp lib/config.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/utils.cpp -$(libcppdir)/valueflow.o: lib/valueflow.cpp lib/analyzer.h lib/astutils.h lib/calculate.h lib/check.h lib/checkuninitvar.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/forwardanalyzer.h lib/importproject.h lib/infer.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/programmemory.h lib/reverseanalyzer.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/valueptr.h lib/vfvalue.h +$(libcppdir)/valueflow.o: lib/valueflow.cpp lib/analyzer.h lib/astutils.h lib/calculate.h lib/check.h lib/checkuninitvar.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/forwardanalyzer.h lib/importproject.h lib/infer.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/programmemory.h lib/reverseanalyzer.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/valueptr.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/valueflow.cpp $(libcppdir)/vfvalue.o: lib/vfvalue.cpp lib/config.h lib/errortypes.h lib/mathlib.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/vfvalue.h diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 8b603ad5a..9510299a1 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -544,7 +544,11 @@ unsigned int CppCheck::check(const std::string &path) Tokenizer tokenizer(&mSettings, this); tokenizer.list.appendFileIfNew(path); clangimport::parseClangAstDump(&tokenizer, ast); - ValueFlow::setValues(&tokenizer.list, const_cast(tokenizer.getSymbolDatabase()), this, &mSettings); + ValueFlow::setValues(&tokenizer.list, + const_cast(tokenizer.getSymbolDatabase()), + this, + &mSettings, + &s_timerResults); if (mSettings.debugnormal) tokenizer.printDebugOutput(1); checkNormalTokens(tokenizer); diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 9bde7e1c8..aeb641def 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -3316,9 +3316,9 @@ bool Tokenizer::simplifyTokens1(const std::string &configuration) if (doValueFlow) { if (mTimerResults) { Timer t("Tokenizer::simplifyTokens1::ValueFlow", mSettings->showtime, mTimerResults); - ValueFlow::setValues(&list, mSymbolDatabase, mErrorLogger, mSettings); + ValueFlow::setValues(&list, mSymbolDatabase, mErrorLogger, mSettings, mTimerResults); } else { - ValueFlow::setValues(&list, mSymbolDatabase, mErrorLogger, mSettings); + ValueFlow::setValues(&list, mSymbolDatabase, mErrorLogger, mSettings, mTimerResults); } } diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 2aa5722d9..b422cd459 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -97,6 +97,7 @@ #include "sourcelocation.h" #include "standards.h" #include "symboldatabase.h" +#include "timer.h" #include "token.h" #include "tokenlist.h" #include "utils.h" @@ -106,6 +107,7 @@ #include #include #include +#include #include #include #include @@ -9071,154 +9073,255 @@ const ValueFlow::Value *ValueFlow::valueFlowConstantFoldAST(Token *expr, const S return expr && expr->hasKnownValue() ? &expr->values().front() : nullptr; } -static std::size_t getTotalValues(TokenList *tokenlist) +struct ValueFlowState { + explicit ValueFlowState(TokenList* tokenlist = nullptr, + SymbolDatabase* symboldatabase = nullptr, + ErrorLogger* errorLogger = nullptr, + const Settings* settings = nullptr) + : tokenlist(tokenlist), symboldatabase(symboldatabase), errorLogger(errorLogger), settings(settings) + {} + + TokenList* tokenlist = nullptr; + SymbolDatabase* symboldatabase = nullptr; + ErrorLogger* errorLogger = nullptr; + const Settings* settings = nullptr; + std::set skippedFunctions = {}; +}; + +struct ValueFlowPass { + ValueFlowPass() = default; + ValueFlowPass(const ValueFlowPass&) = default; + // Name of pass + virtual const char* name() const = 0; + // Run the pass + virtual void run(const ValueFlowState& state) const = 0; + // Returns true if pass needs C++ + virtual bool cpp() const = 0; + virtual ~ValueFlowPass() noexcept {} +}; + +struct ValueFlowPassRunner { + using Clock = std::chrono::steady_clock; + using TimePoint = std::chrono::time_point; + explicit ValueFlowPassRunner(ValueFlowState state, TimerResultsIntf* timerResults = nullptr) + : state(std::move(state)), stop(TimePoint::max()), timerResults(timerResults) + { + setSkippedFunctions(); + setStopTime(); + } + + bool run_once(std::initializer_list> passes) const + { + return std::any_of(passes.begin(), passes.end(), [&](const ValuePtr& pass) { + return run(pass); + }); + } + + bool run(std::initializer_list> passes) const + { + std::size_t values = 0; + std::size_t n = state.settings->valueFlowMaxIterations; + while (n > 0 && values != getTotalValues()) { + values = getTotalValues(); + if (std::any_of(passes.begin(), passes.end(), [&](const ValuePtr& pass) { + return run(pass); + })) + return true; + } + if (state.settings->debugwarnings) { + if (n == 0 && values != getTotalValues()) { + ErrorMessage::FileLocation loc; + loc.setfile(state.tokenlist->getFiles()[0]); + ErrorMessage errmsg({std::move(loc)}, + emptyString, + Severity::debug, + "ValueFlow maximum iterations exceeded", + "valueFlowMaxIterations", + Certainty::normal); + state.errorLogger->reportErr(errmsg); + } + } + return false; + } + + bool run(const ValuePtr& pass) const + { + auto start = Clock::now(); + if (start > stop) + return true; + if (!state.tokenlist->isCPP() && pass->cpp()) + return false; + if (timerResults) { + Timer t(pass->name(), state.settings->showtime, timerResults); + pass->run(state); + } else { + pass->run(state); + } + return false; + } + + std::size_t getTotalValues() const + { + std::size_t n = 1; + for (Token* tok = state.tokenlist->front(); tok; tok = tok->next()) + n += tok->values().size(); + return n; + } + + void setSkippedFunctions() + { + if (state.settings->performanceValueFlowMaxIfCount > 0) { + for (const Scope* functionScope : state.symboldatabase->functionScopes) { + int countIfScopes = 0; + std::vector scopes{functionScope}; + while (!scopes.empty()) { + const Scope* s = scopes.back(); + scopes.pop_back(); + for (const Scope* s2 : s->nestedList) { + scopes.emplace_back(s2); + if (s2->type == Scope::ScopeType::eIf) + ++countIfScopes; + } + } + if (countIfScopes > state.settings->performanceValueFlowMaxIfCount) { + state.skippedFunctions.emplace(functionScope); + + if (state.settings->severity.isEnabled(Severity::information)) { + const std::string& functionName = functionScope->className; + const std::list callstack( + 1, + ErrorMessage::FileLocation(functionScope->bodyStart, state.tokenlist)); + const ErrorMessage errmsg(callstack, + state.tokenlist->getSourceFilePath(), + Severity::information, + "ValueFlow analysis is limited in " + functionName + + ". Use --check-level=exhaustive if full analysis is wanted.", + "checkLevelNormal", + Certainty::normal); + state.errorLogger->reportErr(errmsg); + } + } + } + } + } + + void setStopTime() + { + if (state.settings->performanceValueFlowMaxTime >= 0) + stop = Clock::now() + std::chrono::seconds{state.settings->performanceValueFlowMaxTime}; + } + + ValueFlowState state; + TimePoint stop; + TimerResultsIntf* timerResults; +}; + +template +struct ValueFlowPassAdaptor : ValueFlowPass { + const char* mName = nullptr; + bool mCPP = false; + F mRun; + ValueFlowPassAdaptor(const char* pname, bool pcpp, F prun) : mName(pname), mCPP(pcpp), mRun(prun) {} + const char* name() const override { + return mName; + } + void run(const ValueFlowState& state) const override + { + mRun(state.tokenlist, state.symboldatabase, state.errorLogger, state.settings, state.skippedFunctions); + } + bool cpp() const override { + return mCPP; + } +}; + +template +ValueFlowPassAdaptor makeValueFlowPassAdaptor(const char* name, bool cpp, F run) { - std::size_t n = 1; - for (Token *tok = tokenlist->front(); tok; tok = tok->next()) - n += tok->values().size(); - return n; + return {name, cpp, run}; } -static std::uint64_t getValueFlowStopTime(const Settings* settings) { - if (settings->performanceValueFlowMaxTime >= 0) - return std::time(nullptr) + settings->performanceValueFlowMaxTime; - return ~0ULL; -} +#define VALUEFLOW_ADAPTOR(cpp, ...) \ + makeValueFlowPassAdaptor(#__VA_ARGS__, \ + cpp, \ + [](TokenList* tokenlist, \ + SymbolDatabase* symboldatabase, \ + ErrorLogger* errorLogger, \ + const Settings* settings, \ + const std::set& skippedFunctions) { \ + (void)tokenlist; \ + (void)symboldatabase; \ + (void)errorLogger; \ + (void)settings; \ + (void)skippedFunctions; \ + __VA_ARGS__; \ + }) -void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings) +#define VFA(...) VALUEFLOW_ADAPTOR(false, __VA_ARGS__) +#define VFA_CPP(...) VALUEFLOW_ADAPTOR(true, __VA_ARGS__) + +void ValueFlow::setValues(TokenList* tokenlist, + SymbolDatabase* symboldatabase, + ErrorLogger* errorLogger, + const Settings* settings, + TimerResultsIntf* timerResults) { - for (Token *tok = tokenlist->front(); tok; tok = tok->next()) + for (Token* tok = tokenlist->front(); tok; tok = tok->next()) tok->clearValueFlow(); - valueFlowEnumValue(symboldatabase, settings); - valueFlowNumber(tokenlist, settings); - valueFlowString(tokenlist, settings); - valueFlowArray(tokenlist, settings); - valueFlowUnknownFunctionReturn(tokenlist, settings); - valueFlowGlobalConstVar(tokenlist, settings); - valueFlowEnumValue(symboldatabase, settings); - valueFlowNumber(tokenlist, settings); - valueFlowGlobalStaticVar(tokenlist, settings); - valueFlowPointerAlias(tokenlist, settings); - valueFlowLifetime(tokenlist, symboldatabase, errorLogger, settings); - valueFlowSymbolic(tokenlist, symboldatabase, settings); - valueFlowBitAnd(tokenlist, settings); - valueFlowSameExpressions(tokenlist, settings); - valueFlowConditionExpressions(tokenlist, symboldatabase, errorLogger, *settings); + ValueFlowPassRunner runner{ValueFlowState{tokenlist, symboldatabase, errorLogger, settings}, timerResults}; + runner.run_once({ + VFA(valueFlowEnumValue(symboldatabase, settings)), + VFA(valueFlowNumber(tokenlist, settings)), + VFA(valueFlowString(tokenlist, settings)), + VFA(valueFlowArray(tokenlist, settings)), + VFA(valueFlowUnknownFunctionReturn(tokenlist, settings)), + VFA(valueFlowGlobalConstVar(tokenlist, settings)), + VFA(valueFlowEnumValue(symboldatabase, settings)), + VFA(valueFlowNumber(tokenlist, settings)), + VFA(valueFlowGlobalStaticVar(tokenlist, settings)), + VFA(valueFlowPointerAlias(tokenlist, settings)), + VFA(valueFlowLifetime(tokenlist, symboldatabase, errorLogger, settings)), + VFA(valueFlowSymbolic(tokenlist, symboldatabase, settings)), + VFA(valueFlowBitAnd(tokenlist, settings)), + VFA(valueFlowSameExpressions(tokenlist, settings)), + VFA(valueFlowConditionExpressions(tokenlist, symboldatabase, errorLogger, *settings)), + }); - const std::uint64_t stopTime = getValueFlowStopTime(settings); + runner.run({ + VFA(valueFlowImpossibleValues(tokenlist, settings)), + VFA(valueFlowSymbolicOperators(symboldatabase, settings)), + VFA(valueFlowCondition(SymbolicConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings, skippedFunctions)), + VFA(valueFlowSymbolicInfer(symboldatabase, settings)), + VFA(valueFlowArrayBool(tokenlist, settings)), + VFA(valueFlowArrayElement(tokenlist, settings)), + VFA(valueFlowRightShift(tokenlist, settings)), + VFA(valueFlowAfterAssign(tokenlist, symboldatabase, errorLogger, settings, skippedFunctions)), + VFA_CPP(valueFlowAfterSwap(tokenlist, symboldatabase, errorLogger, settings)), + VFA(valueFlowCondition(SimpleConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings, skippedFunctions)), + VFA(valueFlowInferCondition(tokenlist, settings)), + VFA(valueFlowSwitchVariable(tokenlist, symboldatabase, errorLogger, settings)), + VFA(valueFlowForLoop(tokenlist, symboldatabase, errorLogger, settings)), + VFA(valueFlowSubFunction(tokenlist, symboldatabase, errorLogger, *settings)), + VFA(valueFlowFunctionReturn(tokenlist, errorLogger, settings)), + VFA(valueFlowLifetime(tokenlist, symboldatabase, errorLogger, settings)), + VFA(valueFlowFunctionDefaultParameter(tokenlist, symboldatabase, settings)), + VFA(valueFlowUninit(tokenlist, symboldatabase, settings)), + VFA_CPP(valueFlowAfterMove(tokenlist, symboldatabase, settings)), + VFA_CPP(valueFlowSmartPointer(tokenlist, errorLogger, settings)), + VFA_CPP(valueFlowIterators(tokenlist, settings)), + VFA_CPP( + valueFlowCondition(IteratorConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings, skippedFunctions)), + VFA_CPP(valueFlowIteratorInfer(tokenlist, settings)), + VFA_CPP(valueFlowContainerSize(tokenlist, symboldatabase, errorLogger, settings, skippedFunctions)), + VFA_CPP( + valueFlowCondition(ContainerConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings, skippedFunctions)), + VFA(valueFlowSafeFunctions(tokenlist, symboldatabase, settings)), + }); - std::set skippedFunctions; - if (settings->performanceValueFlowMaxIfCount > 0) { - for (const Scope* functionScope: symboldatabase->functionScopes) { - int countIfScopes = 0; - std::vector scopes{functionScope}; - while (!scopes.empty()) { - const Scope* s = scopes.back(); - scopes.pop_back(); - for (const Scope* s2: s->nestedList) { - scopes.emplace_back(s2); - if (s2->type == Scope::ScopeType::eIf) - ++countIfScopes; - } - } - if (countIfScopes > settings->performanceValueFlowMaxIfCount) { - skippedFunctions.emplace(functionScope); - - if (settings->severity.isEnabled(Severity::information)) { - const std::string& functionName = functionScope->className; - const std::list callstack(1, ErrorMessage::FileLocation(functionScope->bodyStart, tokenlist)); - const ErrorMessage errmsg(callstack, tokenlist->getSourceFilePath(), Severity::information, - "ValueFlow analysis is limited in " + functionName + ". Use --check-level=exhaustive if full analysis is wanted.", - "checkLevelNormal", Certainty::normal); - errorLogger->reportErr(errmsg); - } - } - } - } - - std::size_t values = 0; - std::size_t n = settings->valueFlowMaxIterations; - while (n > 0 && values != getTotalValues(tokenlist)) { - values = getTotalValues(tokenlist); - - if (std::time(nullptr) < stopTime) - valueFlowImpossibleValues(tokenlist, settings); - if (std::time(nullptr) < stopTime) - valueFlowSymbolicOperators(symboldatabase, settings); - if (std::time(nullptr) < stopTime) - valueFlowCondition(SymbolicConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings, skippedFunctions); - if (std::time(nullptr) < stopTime) - valueFlowSymbolicInfer(symboldatabase, settings); - if (std::time(nullptr) < stopTime) - valueFlowArrayBool(tokenlist, settings); - if (std::time(nullptr) < stopTime) - valueFlowArrayElement(tokenlist, settings); - if (std::time(nullptr) < stopTime) - valueFlowRightShift(tokenlist, settings); - if (std::time(nullptr) < stopTime) - valueFlowAfterAssign(tokenlist, symboldatabase, errorLogger, settings, skippedFunctions); - if (std::time(nullptr) < stopTime) - valueFlowAfterSwap(tokenlist, symboldatabase, errorLogger, settings); - if (std::time(nullptr) < stopTime) - valueFlowCondition(SimpleConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings, skippedFunctions); - if (std::time(nullptr) < stopTime) - valueFlowInferCondition(tokenlist, settings); - if (std::time(nullptr) < stopTime) - valueFlowSwitchVariable(tokenlist, symboldatabase, errorLogger, settings); - if (std::time(nullptr) < stopTime) - valueFlowForLoop(tokenlist, symboldatabase, errorLogger, settings); - if (std::time(nullptr) < stopTime) - valueFlowSubFunction(tokenlist, symboldatabase, errorLogger, *settings); - if (std::time(nullptr) < stopTime) - valueFlowFunctionReturn(tokenlist, errorLogger, settings); - if (std::time(nullptr) < stopTime) - valueFlowLifetime(tokenlist, symboldatabase, errorLogger, settings); - if (std::time(nullptr) < stopTime) - valueFlowFunctionDefaultParameter(tokenlist, symboldatabase, settings); - if (std::time(nullptr) < stopTime) - valueFlowUninit(tokenlist, symboldatabase, settings); - - if (tokenlist->isCPP()) { - if (std::time(nullptr) < stopTime) - valueFlowAfterMove(tokenlist, symboldatabase, settings); - if (std::time(nullptr) < stopTime) - valueFlowSmartPointer(tokenlist, errorLogger, settings); - if (std::time(nullptr) < stopTime) - valueFlowIterators(tokenlist, settings); - if (std::time(nullptr) < stopTime) - valueFlowCondition(IteratorConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings, skippedFunctions); - if (std::time(nullptr) < stopTime) - valueFlowIteratorInfer(tokenlist, settings); - if (std::time(nullptr) < stopTime) - valueFlowContainerSize(tokenlist, symboldatabase, errorLogger, settings, skippedFunctions); - if (std::time(nullptr) < stopTime) - valueFlowCondition(ContainerConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings, skippedFunctions); - } - if (std::time(nullptr) < stopTime) - valueFlowSafeFunctions(tokenlist, symboldatabase, settings); - n--; - } - - if (settings->debugwarnings) { - if (n == 0 && values != getTotalValues(tokenlist)) { - ErrorMessage::FileLocation loc; - loc.setfile(tokenlist->getFiles()[0]); - ErrorMessage errmsg({std::move(loc)}, - emptyString, - Severity::debug, - "ValueFlow maximum iterations exceeded", - "valueFlowMaxIterations", - Certainty::normal); - errorLogger->reportErr(errmsg); - } - } - - if (std::time(nullptr) < stopTime) - valueFlowDynamicBufferSize(tokenlist, symboldatabase, settings); - - if (std::time(nullptr) < stopTime) - valueFlowDebug(tokenlist, errorLogger, settings); + runner.run_once({ + VFA(valueFlowDynamicBufferSize(tokenlist, symboldatabase, settings)), + VFA(valueFlowDebug(tokenlist, errorLogger, settings)), + }); } std::string ValueFlow::eitherTheConditionIsRedundant(const Token *condition) diff --git a/lib/valueflow.h b/lib/valueflow.h index 9ff9c753a..c7298bb31 100644 --- a/lib/valueflow.h +++ b/lib/valueflow.h @@ -36,6 +36,7 @@ class ErrorLogger; struct InferModel; class Settings; class SymbolDatabase; +class TimerResultsIntf; class Token; class TokenList; class ValueType; @@ -50,7 +51,11 @@ namespace ValueFlow { const Value * valueFlowConstantFoldAST(Token *expr, const Settings *settings); /// Perform valueflow analysis. - void setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings); + void setValues(TokenList* tokenlist, + SymbolDatabase* symboldatabase, + ErrorLogger* errorLogger, + const Settings* settings, + TimerResultsIntf* timerResults); std::string eitherTheConditionIsRedundant(const Token *condition);