--performance-valueflow-max-if-count: review comments

This commit is contained in:
Daniel Marjamäki 2023-04-08 13:04:41 +02:00
parent 39f94f32f9
commit 89a95ddc8f
5 changed files with 83 additions and 16 deletions

View File

@ -1220,8 +1220,7 @@ void CmdLineParser::printHelp()
" the number of possible control flow paths through that\n" " the number of possible control flow paths through that\n"
" function explodes and that can lead to very long analysis\n" " function explodes and that can lead to very long analysis\n"
" time. Valueflow is limited in functions that have more\n" " time. Valueflow is limited in functions that have more\n"
" than <limit> conditional scopes. The default limit is\n" " than <limit> conditional scopes.\n"
" 100, which few functions will reach.\n"
" --platform=<type>, --platform=<file>\n" " --platform=<type>, --platform=<file>\n"
" Specifies platform specific types and sizes. The\n" " Specifies platform specific types and sizes. The\n"
" available builtin platforms are:\n" " available builtin platforms are:\n"

View File

@ -8501,10 +8501,17 @@ static void valueFlowContainerSetTokValue(TokenList* tokenlist, const Settings*
} }
} }
static const Scope* getFunctionScope(const Scope* scope) {
while (scope && scope->type != Scope::ScopeType::eFunction)
scope = scope->nestedIn;
return scope;
}
static void valueFlowContainerSize(TokenList* tokenlist, static void valueFlowContainerSize(TokenList* tokenlist,
SymbolDatabase* symboldatabase, SymbolDatabase* symboldatabase,
ErrorLogger* /*errorLogger*/, ErrorLogger* /*errorLogger*/,
const Settings* settings) const Settings* settings,
const std::set<const Scope*>& skippedFunctions)
{ {
// declaration // declaration
for (const Variable *var : symboldatabase->variableList()) { for (const Variable *var : symboldatabase->variableList()) {
@ -8516,6 +8523,8 @@ static void valueFlowContainerSize(TokenList* tokenlist,
continue; continue;
if (!astIsContainer(var->nameToken())) if (!astIsContainer(var->nameToken()))
continue; continue;
if (skippedFunctions.count(getFunctionScope(var->scope())))
continue;
bool known = true; bool known = true;
int size = 0; int size = 0;
@ -9077,7 +9086,7 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase,
const std::uint64_t stopTime = getValueFlowStopTime(settings); const std::uint64_t stopTime = getValueFlowStopTime(settings);
std::set<const Scope*> skippedFunctions; std::set<const Scope*> skippedFunctions;
if (settings->performanceValueFlowMaxIfCount < 1000) { if (settings->performanceValueFlowMaxIfCount > 0) {
for (const Scope* functionScope: symboldatabase->functionScopes) { for (const Scope* functionScope: symboldatabase->functionScopes) {
int countIfScopes = 0; int countIfScopes = 0;
std::vector<const Scope*> scopes{functionScope}; std::vector<const Scope*> scopes{functionScope};
@ -9093,15 +9102,17 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase,
if (countIfScopes > settings->performanceValueFlowMaxIfCount) { if (countIfScopes > settings->performanceValueFlowMaxIfCount) {
skippedFunctions.emplace(functionScope); skippedFunctions.emplace(functionScope);
const std::string& functionName = functionScope->className; if (settings->severity.isEnabled(Severity::information)) {
const std::list<ErrorMessage::FileLocation> callstack(1, ErrorMessage::FileLocation(functionScope->bodyStart, tokenlist)); const std::string& functionName = functionScope->className;
const ErrorMessage errmsg(callstack, tokenlist->getSourceFilePath(), Severity::information, const std::list<ErrorMessage::FileLocation> callstack(1, ErrorMessage::FileLocation(functionScope->bodyStart, tokenlist));
"ValueFlow analysis is limited in " + functionName + " because if-count in function " + const ErrorMessage errmsg(callstack, tokenlist->getSourceFilePath(), Severity::information,
std::to_string(countIfScopes) + " exceeds limit " + "ValueFlow analysis is limited in " + functionName + " because if-count in function " +
std::to_string(settings->performanceValueFlowMaxIfCount) + ". The limit can be adjusted with " std::to_string(countIfScopes) + " exceeds limit " +
"--performance-valueflow-max-if-count. Increasing the if-count limit will likely increase the " std::to_string(settings->performanceValueFlowMaxIfCount) + ". The limit can be adjusted with "
"analysis time.", "performanceValueflowMaxIfCountExceeded", Certainty::normal); "--performance-valueflow-max-if-count. Increasing the if-count limit will likely increase the "
errorLogger->reportErr(errmsg); "analysis time.", "performanceValueflowMaxIfCountExceeded", Certainty::normal);
errorLogger->reportErr(errmsg);
}
} }
} }
} }
@ -9159,8 +9170,8 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase,
valueFlowCondition(IteratorConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings, skippedFunctions); valueFlowCondition(IteratorConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings, skippedFunctions);
if (std::time(nullptr) < stopTime) if (std::time(nullptr) < stopTime)
valueFlowIteratorInfer(tokenlist, settings); valueFlowIteratorInfer(tokenlist, settings);
if (std::time(nullptr) < stopTime && skippedFunctions.empty()) if (std::time(nullptr) < stopTime)
valueFlowContainerSize(tokenlist, symboldatabase, errorLogger, settings); valueFlowContainerSize(tokenlist, symboldatabase, errorLogger, settings, skippedFunctions);
if (std::time(nullptr) < stopTime) if (std::time(nullptr) < stopTime)
valueFlowCondition(ContainerConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings, skippedFunctions); valueFlowCondition(ContainerConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings, skippedFunctions);
} }

View File

@ -957,6 +957,34 @@ Example usage:
./cppcheck gui/test.cpp --xml 2> err.xml ./cppcheck gui/test.cpp --xml 2> err.xml
htmlreport/cppcheck-htmlreport --file=err.xml --report-dir=test1 --source-dir=. htmlreport/cppcheck-htmlreport --file=err.xml --report-dir=test1 --source-dir=.
# Performance - Limit analysis
## Limit preprocessor configurations
For performance reasons it might be a good idea to limit preprocessor configurations to check.
## Limit ValueFlow: max if count
The command line option `--performance-valueflow-max-if-count` adjusts the max count for number of if in a function.
When that limit is exceeded there is a limitation of data flow in that function. It is not drastic:
* Analysis of other functions are not affected.
* It's only for some specific data flow analysis, we have data flow analysis that is always executed.
* All checks are always executed. There can still be plenty of warnings in the limited function.
There is data flow analysis that slows down exponentially when number of if increase. And the limit is intended to avoid that
analysis time explodes.
## GUI options
In the GUI there are various options to limit analysis.
In the GUI:
* Open the project dialog.
* In the "Analysis" tab there are several options.
If you want to use these limitations on the command line also you can import the GUI project file with --project.
# Cppcheck Premium # Cppcheck Premium
## Bug hunting ## Bug hunting

View File

@ -12,4 +12,4 @@ release notes for cppcheck-2.11
- `constVariable` - `constVariable`
- `constVariableReference` - `constVariableReference`
- `constVariablePointer` - `constVariablePointer`
- Limit valueflow analysis in function if the "if" count is high. By default max limit is 100. Limit can be tweaked with --performance-valueflow-max-if-count

View File

@ -164,6 +164,8 @@ private:
TEST_CASE(valueFlowImpossibleMinMax); TEST_CASE(valueFlowImpossibleMinMax);
TEST_CASE(valueFlowImpossibleUnknownConstant); TEST_CASE(valueFlowImpossibleUnknownConstant);
TEST_CASE(valueFlowContainerEqual); TEST_CASE(valueFlowContainerEqual);
TEST_CASE(performanceIfCount);
} }
static bool isNotTokValue(const ValueFlow::Value &val) { static bool isNotTokValue(const ValueFlow::Value &val) {
@ -7942,6 +7944,33 @@ private:
ASSERT_EQUALS(false, testValueOfX(code, 5U, 1)); ASSERT_EQUALS(false, testValueOfX(code, 5U, 1));
ASSERT_EQUALS(false, testValueOfX(code, 5U, 0)); ASSERT_EQUALS(false, testValueOfX(code, 5U, 0));
} }
void performanceIfCount() {
Settings s(settings);
s.performanceValueFlowMaxIfCount = 1;
const char *code;
code = "int f() {\n"
" if (x>0){}\n"
" if (y>0){}\n"
" int a = 14;\n"
" return a+1;\n"
"}\n";
ASSERT_EQUALS(0U, tokenValues(code, "+", &s).size());
ASSERT_EQUALS(1U, tokenValues(code, "+").size());
// Do not skip all functions
code = "void g(int i) {\n"
" if (i == 1) {}\n"
" if (i == 2) {}\n"
"}\n"
"int f() {\n"
" std::vector<int> v;\n"
" return v.front();\n"
"}\n";
ASSERT_EQUALS(1U, tokenValues(code, "v .", &s).size());
}
}; };
REGISTER_TEST(TestValueFlow) REGISTER_TEST(TestValueFlow)