Skip to content

Commit 0752d12

Browse files
author
Endre Fulop
committed
[analyzer] Add analyzer option to limit the number of imported TUs
Summary: During CTU analysis of complex projects, the loaded AST-contents of imported TUs can grow bigger than available system memory. This option introduces a threshold on the number of TUs to be imported for a single TU in order to prevent such cases. Differential Revision: https://reviews.llvm.org/D59798 llvm-svn: 365314
1 parent 1602058 commit 0752d12

File tree

6 files changed

+60
-13
lines changed

6 files changed

+60
-13
lines changed

clang/include/clang/CrossTU/CrossTranslationUnit.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ enum class index_error_code {
4545
failed_to_generate_usr,
4646
triple_mismatch,
4747
lang_mismatch,
48-
lang_dialect_mismatch
48+
lang_dialect_mismatch,
49+
load_threshold_reached
4950
};
5051

5152
class IndexError : public llvm::ErrorInfo<IndexError> {
@@ -134,7 +135,8 @@ class CrossTranslationUnitContext {
134135
/// A definition with the same declaration will be looked up in the
135136
/// index file which should be in the \p CrossTUDir directory, called
136137
/// \p IndexName. In case the declaration is found in the index the
137-
/// corresponding AST file will be loaded.
138+
/// corresponding AST file will be loaded. If the number of TUs imported
139+
/// reaches \p CTULoadTreshold, no loading is performed.
138140
///
139141
/// \return Returns a pointer to the ASTUnit that contains the definition of
140142
/// the looked up name or an Error.
@@ -182,6 +184,10 @@ class CrossTranslationUnitContext {
182184
CompilerInstance &CI;
183185
ASTContext &Context;
184186
std::shared_ptr<ASTImporterSharedState> ImporterSharedSt;
187+
/// \p CTULoadTreshold should serve as an upper limit to the number of TUs
188+
/// imported in order to reduce the memory footprint of CTU analysis.
189+
const unsigned CTULoadThreshold;
190+
unsigned NumASTLoaded{0u};
185191
};
186192

187193
} // namespace cross_tu

clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,14 @@ ANALYZER_OPTION(bool, ShouldTrackConditionsDebug, "track-conditions-debug",
300300
"Whether to place an event at each tracked condition.",
301301
false)
302302

303+
ANALYZER_OPTION(unsigned, CTUImportThreshold, "ctu-import-threshold",
304+
"The maximal amount of translation units that is considered "
305+
"for import when inlining functions during CTU analysis. "
306+
"Lowering this threshold can alleviate the memory burder of "
307+
"analysis with many interdependent definitions located in "
308+
"various translation units.",
309+
100u)
310+
303311
//===----------------------------------------------------------------------===//
304312
// Unsinged analyzer options.
305313
//===----------------------------------------------------------------------===//

clang/lib/CrossTU/CrossTranslationUnit.cpp

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ STATISTIC(NumNameConflicts, "The # of imports when the ASTImporter "
4747
STATISTIC(NumTripleMismatch, "The # of triple mismatches");
4848
STATISTIC(NumLangMismatch, "The # of language mismatches");
4949
STATISTIC(NumLangDialectMismatch, "The # of language dialect mismatches");
50+
STATISTIC(NumASTLoadThresholdReached,
51+
"The # of ASTs not loaded because of threshold");
5052

5153
// Same as Triple's equality operator, but we check a field only if that is
5254
// known in both instances.
@@ -106,6 +108,8 @@ class IndexErrorCategory : public std::error_category {
106108
return "Language mismatch";
107109
case index_error_code::lang_dialect_mismatch:
108110
return "Language dialect mismatch";
111+
case index_error_code::load_threshold_reached:
112+
return "Load threshold reached";
109113
}
110114
llvm_unreachable("Unrecognized index_error_code.");
111115
}
@@ -184,7 +188,8 @@ template <typename T> static bool hasBodyOrInit(const T *D) {
184188
}
185189

186190
CrossTranslationUnitContext::CrossTranslationUnitContext(CompilerInstance &CI)
187-
: CI(CI), Context(CI.getASTContext()) {}
191+
: CI(CI), Context(CI.getASTContext()),
192+
CTULoadThreshold(CI.getAnalyzerOpts()->CTUImportThreshold) {}
188193

189194
CrossTranslationUnitContext::~CrossTranslationUnitContext() {}
190195

@@ -232,8 +237,8 @@ llvm::Expected<const T *> CrossTranslationUnitContext::getCrossTUDefinitionImpl(
232237
if (LookupName.empty())
233238
return llvm::make_error<IndexError>(
234239
index_error_code::failed_to_generate_usr);
235-
llvm::Expected<ASTUnit *> ASTUnitOrError =
236-
loadExternalAST(LookupName, CrossTUDir, IndexName, DisplayCTUProgress);
240+
llvm::Expected<ASTUnit *> ASTUnitOrError = loadExternalAST(
241+
LookupName, CrossTUDir, IndexName, DisplayCTUProgress);
237242
if (!ASTUnitOrError)
238243
return ASTUnitOrError.takeError();
239244
ASTUnit *Unit = *ASTUnitOrError;
@@ -342,6 +347,13 @@ llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST(
342347
// a lookup name from a single translation unit. If multiple
343348
// translation units contains decls with the same lookup name an
344349
// error will be returned.
350+
351+
if (NumASTLoaded >= CTULoadThreshold) {
352+
++NumASTLoadThresholdReached;
353+
return llvm::make_error<IndexError>(
354+
index_error_code::load_threshold_reached);
355+
}
356+
345357
ASTUnit *Unit = nullptr;
346358
auto NameUnitCacheEntry = NameASTUnitMap.find(LookupName);
347359
if (NameUnitCacheEntry == NameASTUnitMap.end()) {
@@ -379,6 +391,7 @@ llvm::Expected<ASTUnit *> CrossTranslationUnitContext::loadExternalAST(
379391
ASTUnit::LoadEverything, Diags, CI.getFileSystemOpts()));
380392
Unit = LoadedUnit.get();
381393
FileASTUnitMap[ASTFileName] = std::move(LoadedUnit);
394+
++NumASTLoaded;
382395
if (DisplayCTUProgress) {
383396
llvm::errs() << "CTU loaded AST file: "
384397
<< ASTFileName << "\n";

clang/test/Analysis/analyzer-config.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
// CHECK-NEXT: cplusplus.Move:WarnOn = KnownsAndLocals
2828
// CHECK-NEXT: crosscheck-with-z3 = false
2929
// CHECK-NEXT: ctu-dir = ""
30+
// CHECK-NEXT: ctu-import-threshold = 100
3031
// CHECK-NEXT: ctu-index-name = externalDefMap.txt
3132
// CHECK-NEXT: debug.AnalysisOrder:* = false
3233
// CHECK-NEXT: debug.AnalysisOrder:Bind = false
@@ -90,4 +91,4 @@
9091
// CHECK-NEXT: unroll-loops = false
9192
// CHECK-NEXT: widen-loops = false
9293
// CHECK-NEXT: [stats]
93-
// CHECK-NEXT: num-entries = 87
94+
// CHECK-NEXT: num-entries = 88
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Ensure analyzer option 'ctu-import-threshold' is a recognized option.
2+
//
3+
// RUN: %clang_cc1 -analyze -analyzer-config ctu-import-threshold=30 -verify %s
4+
//
5+
// expected-no-diagnostics

clang/unittests/CrossTU/CrossTranslationUnitTest.cpp

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "clang/CrossTU/CrossTranslationUnit.h"
10+
#include "clang/Frontend/CompilerInstance.h"
1011
#include "clang/AST/ASTConsumer.h"
1112
#include "clang/Frontend/FrontendAction.h"
1213
#include "clang/Tooling/Tooling.h"
@@ -70,12 +71,14 @@ class CTUASTConsumer : public clang::ASTConsumer {
7071
EXPECT_TRUE(llvm::sys::fs::exists(ASTFileName));
7172

7273
// Load the definition from the AST file.
73-
llvm::Expected<const FunctionDecl *> NewFDorError =
74-
CTU.getCrossTUDefinition(FD, "", IndexFileName);
75-
EXPECT_TRUE((bool)NewFDorError);
76-
const FunctionDecl *NewFD = *NewFDorError;
74+
llvm::Expected<const FunctionDecl *> NewFDorError = handleExpected(
75+
CTU.getCrossTUDefinition(FD, "", IndexFileName, false),
76+
[]() { return nullptr; }, [](IndexError &) {});
7777

78-
*Success = NewFD && NewFD->hasBody() && !OrigFDHasBody;
78+
if (NewFDorError) {
79+
const FunctionDecl *NewFD = *NewFDorError;
80+
*Success = NewFD && NewFD->hasBody() && !OrigFDHasBody;
81+
}
7982
}
8083

8184
private:
@@ -85,26 +88,37 @@ class CTUASTConsumer : public clang::ASTConsumer {
8588

8689
class CTUAction : public clang::ASTFrontendAction {
8790
public:
88-
CTUAction(bool *Success) : Success(Success) {}
91+
CTUAction(bool *Success, unsigned OverrideLimit)
92+
: Success(Success), OverrideLimit(OverrideLimit) {}
8993

9094
protected:
9195
std::unique_ptr<clang::ASTConsumer>
9296
CreateASTConsumer(clang::CompilerInstance &CI, StringRef) override {
97+
CI.getAnalyzerOpts()->CTUImportThreshold = OverrideLimit;
9398
return llvm::make_unique<CTUASTConsumer>(CI, Success);
9499
}
95100

96101
private:
97102
bool *Success;
103+
const unsigned OverrideLimit;
98104
};
99105

100106
} // end namespace
101107

102108
TEST(CrossTranslationUnit, CanLoadFunctionDefinition) {
103109
bool Success = false;
104-
EXPECT_TRUE(tooling::runToolOnCode(new CTUAction(&Success), "int f(int);"));
110+
EXPECT_TRUE(
111+
tooling::runToolOnCode(new CTUAction(&Success, 1u), "int f(int);"));
105112
EXPECT_TRUE(Success);
106113
}
107114

115+
TEST(CrossTranslationUnit, RespectsLoadThreshold) {
116+
bool Success = false;
117+
EXPECT_TRUE(
118+
tooling::runToolOnCode(new CTUAction(&Success, 0u), "int f(int);"));
119+
EXPECT_FALSE(Success);
120+
}
121+
108122
TEST(CrossTranslationUnit, IndexFormatCanBeParsed) {
109123
llvm::StringMap<std::string> Index;
110124
Index["a"] = "/b/f1";

0 commit comments

Comments
 (0)