valueflow: limit valueflow in functions that have many ifs

This commit is contained in:
Daniel Marjamäki 2023-04-01 20:58:15 +02:00
parent eb0998dc2c
commit 2359b9ff82
4 changed files with 71 additions and 14 deletions

View File

@ -23,3 +23,5 @@ autoNoType
bailoutUninitVar
*:externals/*/*
performanceValueflowMaxIfCountExceeded

View File

@ -606,6 +606,9 @@ bool CmdLineParser::parseFromArgs(int argc, const char* const argv[])
else if (std::strncmp(argv[i], "--performance-valueflow-max-time=", 33) == 0)
mSettings.performanceValueFlowMaxTime = std::atoi(argv[i] + 33);
else if (std::strncmp(argv[i], "--performance-valueflow-max-if-count=", 37) == 0)
mSettings.performanceValueFlowMaxIfCount = std::atoi(argv[i] + 37);
// Specify platform
else if (std::strncmp(argv[i], "--platform=", 11) == 0) {
const std::string platform(11+argv[i]);
@ -1212,6 +1215,13 @@ void CmdLineParser::printHelp()
" is 2. A larger value will mean more errors can be found\n"
" but also means the analysis will be slower.\n"
" --output-file=<file> Write results to file, rather than standard error.\n"
" --performance-valueflow-max-if-count=<limit>\n"
" If you have many conditional scopes in a function then\n"
" the number of possible control flow paths through that\n"
" function explodes and that can lead to very long analysis\n"
" time. Valueflow is limited in functions that have more\n"
" than <limit> conditional scopes. The default limit is\n"
" 100, which few functions will reach.\n"
" --platform=<type>, --platform=<file>\n"
" Specifies platform specific types and sizes. The\n"
" available builtin platforms are:\n"

View File

@ -254,6 +254,9 @@ public:
/** @brief Experimental: --performance-valueflow-max-time=T */
int performanceValueFlowMaxTime;
/** @brief --performance-valueflow-max-if-count=C */
int performanceValueFlowMaxIfCount = 100;
/** @brief plist output (--plist-output=&lt;dir&gt;) */
std::string plistOutput;

View File

@ -5747,9 +5747,15 @@ static bool intersects(const C1& c1, const C2& c2)
return false;
}
static void valueFlowAfterAssign(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings)
static void valueFlowAfterAssign(TokenList *tokenlist,
SymbolDatabase* symboldatabase,
ErrorLogger *errorLogger,
const Settings *settings,
const std::set<const Scope*>& skippedFunctions)
{
for (const Scope * scope : symboldatabase->functionScopes) {
if (skippedFunctions.count(scope))
continue;
std::unordered_map<nonneg int, std::unordered_set<nonneg int>> backAssigns;
for (Token* tok = const_cast<Token*>(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) {
// Assignment
@ -6051,9 +6057,12 @@ struct ConditionHandler {
void traverseCondition(TokenList* tokenlist,
SymbolDatabase* symboldatabase,
const Settings* settings,
const std::set<const Scope*>& skippedFunctions,
const std::function<void(const Condition& cond, Token* tok, const Scope* scope)>& f) const
{
for (const Scope *scope : symboldatabase->functionScopes) {
if (skippedFunctions.count(scope))
continue;
for (Token *tok = const_cast<Token *>(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) {
if (Token::Match(tok, "if|while|for ("))
continue;
@ -6086,8 +6095,9 @@ struct ConditionHandler {
void beforeCondition(TokenList* tokenlist,
SymbolDatabase* symboldatabase,
ErrorLogger* errorLogger,
const Settings* settings) const {
traverseCondition(tokenlist, symboldatabase, settings, [&](const Condition& cond, Token* tok, const Scope*) {
const Settings* settings,
const std::set<const Scope*>& skippedFunctions) const {
traverseCondition(tokenlist, symboldatabase, settings, skippedFunctions, [&](const Condition& cond, Token* tok, const Scope*) {
if (cond.vartok->exprId() == 0)
return;
@ -6233,8 +6243,9 @@ struct ConditionHandler {
void afterCondition(TokenList* tokenlist,
SymbolDatabase* symboldatabase,
ErrorLogger* errorLogger,
const Settings* settings) const {
traverseCondition(tokenlist, symboldatabase, settings, [&](const Condition& cond, Token* condTok, const Scope* scope) {
const Settings* settings,
const std::set<const Scope*>& skippedFunctions) const {
traverseCondition(tokenlist, symboldatabase, settings, skippedFunctions, [&](const Condition& cond, Token* condTok, const Scope* scope) {
const Token* top = condTok->astTop();
const MathLib::bigint path = cond.getPath();
@ -6564,10 +6575,11 @@ static void valueFlowCondition(const ValuePtr<ConditionHandler>& handler,
TokenList* tokenlist,
SymbolDatabase* symboldatabase,
ErrorLogger* errorLogger,
const Settings* settings)
const Settings* settings,
const std::set<const Scope*>& skippedFunctions)
{
handler->beforeCondition(tokenlist, symboldatabase, errorLogger, settings);
handler->afterCondition(tokenlist, symboldatabase, errorLogger, settings);
handler->beforeCondition(tokenlist, symboldatabase, errorLogger, settings, skippedFunctions);
handler->afterCondition(tokenlist, symboldatabase, errorLogger, settings, skippedFunctions);
}
struct SimpleConditionHandler : ConditionHandler {
@ -8966,6 +8978,36 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase,
const std::uint64_t stopTime = getValueFlowStopTime(settings);
std::set<const Scope*> skippedFunctions;
if (settings->performanceValueFlowMaxIfCount < 1000) {
for (const Scope* functionScope: symboldatabase->functionScopes) {
int countIfScopes = 0;
std::vector<const Scope*> 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);
const std::string& functionName = functionScope->className;
const std::list<ErrorMessage::FileLocation> callstack(1, ErrorMessage::FileLocation(functionScope->bodyStart, tokenlist));
const ErrorMessage errmsg(callstack, tokenlist->getSourceFilePath(), Severity::information,
"ValueFlow analysis is limited in " + functionName + " because if-count in function " +
std::to_string(countIfScopes) + " exceeds limit " +
std::to_string(settings->performanceValueFlowMaxIfCount) + ". The limit can be adjusted with "
"--performance-valueflow-max-if-count. Increasing the if-count limit will likely increase the "
"analysis time.", "performanceValueflowMaxIfCountExceeded", Certainty::normal);
errorLogger->reportErr(errmsg);
}
}
}
std::size_t values = 0;
std::size_t n = settings->valueFlowMaxIterations;
while (n > 0 && values != getTotalValues(tokenlist)) {
@ -8976,7 +9018,7 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase,
if (std::time(nullptr) < stopTime)
valueFlowSymbolicOperators(symboldatabase, settings);
if (std::time(nullptr) < stopTime)
valueFlowCondition(SymbolicConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings);
valueFlowCondition(SymbolicConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings, skippedFunctions);
if (std::time(nullptr) < stopTime)
valueFlowSymbolicInfer(symboldatabase, settings);
if (std::time(nullptr) < stopTime)
@ -8986,11 +9028,11 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase,
if (std::time(nullptr) < stopTime)
valueFlowRightShift(tokenlist, settings);
if (std::time(nullptr) < stopTime)
valueFlowAfterAssign(tokenlist, symboldatabase, errorLogger, settings);
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);
valueFlowCondition(SimpleConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings, skippedFunctions);
if (std::time(nullptr) < stopTime)
valueFlowInferCondition(tokenlist, settings);
if (std::time(nullptr) < stopTime)
@ -9016,13 +9058,13 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase,
if (std::time(nullptr) < stopTime)
valueFlowIterators(tokenlist, settings);
if (std::time(nullptr) < stopTime)
valueFlowCondition(IteratorConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings);
valueFlowCondition(IteratorConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings, skippedFunctions);
if (std::time(nullptr) < stopTime)
valueFlowIteratorInfer(tokenlist, settings);
if (std::time(nullptr) < stopTime)
if (std::time(nullptr) < stopTime && skippedFunctions.empty())
valueFlowContainerSize(tokenlist, symboldatabase, errorLogger, settings);
if (std::time(nullptr) < stopTime)
valueFlowCondition(ContainerConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings);
valueFlowCondition(ContainerConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings, skippedFunctions);
}
if (std::time(nullptr) < stopTime)
valueFlowSafeFunctions(tokenlist, symboldatabase, settings);