Skip to content

Commit 32b8283

Browse files
authored
[analyzer] Set and display CSA analysis entry points as notes on debugging (#84823)
When debugging CSA issues, sometimes it would be useful to have a dedicated note for the analysis entry point, aka. the function name you would need to pass as "-analyze-function=XYZ" to reproduce a specific issue. One way we use (or will use) this downstream is to provide tooling on top of creduce to enhance to supercharge productivity by automatically reduce cases on crashes for example. This will be added only if the "-analyzer-note-analysis-entry-points" is set or the "analyzer-display-progress" is on. This additional entry point marker will be the first "note" if enabled, with the following message: "[debug] analyzing from XYZ". They are prefixed by "[debug]" to remind the CSA developer that this is only meant to be visible for them, for debugging purposes. CPP-5012
1 parent ded6252 commit 32b8283

File tree

11 files changed

+193
-36
lines changed

11 files changed

+193
-36
lines changed

clang/include/clang/Analysis/PathDiagnostic.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,9 @@ class PathDiagnostic : public llvm::FoldingSetNode {
780780
PathDiagnosticLocation UniqueingLoc;
781781
const Decl *UniqueingDecl;
782782

783+
/// The top-level entry point from which this issue was discovered.
784+
const Decl *AnalysisEntryPoint = nullptr;
785+
783786
/// Lines executed in the path.
784787
std::unique_ptr<FilesToLineNumsMap> ExecutedLines;
785788

@@ -788,7 +791,7 @@ class PathDiagnostic : public llvm::FoldingSetNode {
788791
PathDiagnostic(StringRef CheckerName, const Decl *DeclWithIssue,
789792
StringRef bugtype, StringRef verboseDesc, StringRef shortDesc,
790793
StringRef category, PathDiagnosticLocation LocationToUnique,
791-
const Decl *DeclToUnique,
794+
const Decl *DeclToUnique, const Decl *AnalysisEntryPoint,
792795
std::unique_ptr<FilesToLineNumsMap> ExecutedLines);
793796
~PathDiagnostic();
794797

@@ -852,6 +855,9 @@ class PathDiagnostic : public llvm::FoldingSetNode {
852855
return *ExecutedLines;
853856
}
854857

858+
/// Get the top-level entry point from which this issue was discovered.
859+
const Decl *getAnalysisEntryPoint() const { return AnalysisEntryPoint; }
860+
855861
/// Return the semantic context where an issue occurred. If the
856862
/// issue occurs along a path, this represents the "central" area
857863
/// where the bug manifests.

clang/include/clang/Driver/Options.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6688,6 +6688,9 @@ def analyzer_opt_analyze_headers : Flag<["-"], "analyzer-opt-analyze-headers">,
66886688
def analyzer_display_progress : Flag<["-"], "analyzer-display-progress">,
66896689
HelpText<"Emit verbose output about the analyzer's progress">,
66906690
MarshallingInfoFlag<AnalyzerOpts<"AnalyzerDisplayProgress">>;
6691+
def analyzer_note_analysis_entry_points : Flag<["-"], "analyzer-note-analysis-entry-points">,
6692+
HelpText<"Add a note for each bug report to denote their analysis entry points">,
6693+
MarshallingInfoFlag<AnalyzerOpts<"AnalyzerNoteAnalysisEntryPoints">>;
66916694
def analyze_function : Separate<["-"], "analyze-function">,
66926695
HelpText<"Run analysis on specific function (for C++ include parameters in name)">,
66936696
MarshallingInfoString<AnalyzerOpts<"AnalyzeSpecificFunction">>;

clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ class AnalyzerOptions : public RefCountedBase<AnalyzerOptions> {
227227
unsigned ShouldEmitErrorsOnInvalidConfigValue : 1;
228228
unsigned AnalyzeAll : 1;
229229
unsigned AnalyzerDisplayProgress : 1;
230+
unsigned AnalyzerNoteAnalysisEntryPoints : 1;
230231

231232
unsigned eagerlyAssumeBinOpBifurcation : 1;
232233

@@ -291,10 +292,10 @@ class AnalyzerOptions : public RefCountedBase<AnalyzerOptions> {
291292
ShowCheckerOptionDeveloperList(false), ShowEnabledCheckerList(false),
292293
ShowConfigOptionsList(false),
293294
ShouldEmitErrorsOnInvalidConfigValue(false), AnalyzeAll(false),
294-
AnalyzerDisplayProgress(false), eagerlyAssumeBinOpBifurcation(false),
295-
TrimGraph(false), visualizeExplodedGraphWithGraphViz(false),
296-
UnoptimizedCFG(false), PrintStats(false), NoRetryExhausted(false),
297-
AnalyzerWerror(false) {}
295+
AnalyzerDisplayProgress(false), AnalyzerNoteAnalysisEntryPoints(false),
296+
eagerlyAssumeBinOpBifurcation(false), TrimGraph(false),
297+
visualizeExplodedGraphWithGraphViz(false), UnoptimizedCFG(false),
298+
PrintStats(false), NoRetryExhausted(false), AnalyzerWerror(false) {}
298299

299300
/// Interprets an option's string value as a boolean. The "true" string is
300301
/// interpreted as true and the "false" string is interpreted as false.

clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,9 @@ class BugReporter {
586586
private:
587587
BugReporterData& D;
588588

589+
/// The top-level entry point for the issue to be reported.
590+
const Decl *AnalysisEntryPoint = nullptr;
591+
589592
/// Generate and flush the diagnostics for the given bug report.
590593
void FlushReport(BugReportEquivClass& EQ);
591594

@@ -623,6 +626,14 @@ class BugReporter {
623626

624627
Preprocessor &getPreprocessor() { return D.getPreprocessor(); }
625628

629+
/// Get the top-level entry point for the issue to be reported.
630+
const Decl *getAnalysisEntryPoint() const { return AnalysisEntryPoint; }
631+
632+
void setAnalysisEntryPoint(const Decl *EntryPoint) {
633+
assert(EntryPoint);
634+
AnalysisEntryPoint = EntryPoint;
635+
}
636+
626637
/// Add the given report to the set of reports tracked by BugReporter.
627638
///
628639
/// The reports are usually generated by the checkers. Further, they are
@@ -713,6 +724,7 @@ class BugReporterContext {
713724
virtual ~BugReporterContext() = default;
714725

715726
PathSensitiveBugReporter& getBugReporter() { return BR; }
727+
const PathSensitiveBugReporter &getBugReporter() const { return BR; }
716728

717729
ProgramStateManager& getStateManager() const {
718730
return BR.getStateManager();

clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ class ExprEngine {
187187

188188
/// Returns true if there is still simulation state on the worklist.
189189
bool ExecuteWorkList(const LocationContext *L, unsigned Steps = 150000) {
190+
assert(L->inTopFrame());
191+
BR.setAnalysisEntryPoint(L->getDecl());
190192
return Engine.ExecuteWorkList(L, Steps, nullptr);
191193
}
192194

clang/lib/Analysis/PathDiagnostic.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,17 @@ PathDiagnostic::PathDiagnostic(
115115
StringRef CheckerName, const Decl *declWithIssue, StringRef bugtype,
116116
StringRef verboseDesc, StringRef shortDesc, StringRef category,
117117
PathDiagnosticLocation LocationToUnique, const Decl *DeclToUnique,
118+
const Decl *AnalysisEntryPoint,
118119
std::unique_ptr<FilesToLineNumsMap> ExecutedLines)
119120
: CheckerName(CheckerName), DeclWithIssue(declWithIssue),
120121
BugType(StripTrailingDots(bugtype)),
121122
VerboseDesc(StripTrailingDots(verboseDesc)),
122123
ShortDesc(StripTrailingDots(shortDesc)),
123124
Category(StripTrailingDots(category)), UniqueingLoc(LocationToUnique),
124-
UniqueingDecl(DeclToUnique), ExecutedLines(std::move(ExecutedLines)),
125-
path(pathImpl) {}
125+
UniqueingDecl(DeclToUnique), AnalysisEntryPoint(AnalysisEntryPoint),
126+
ExecutedLines(std::move(ExecutedLines)), path(pathImpl) {
127+
assert(AnalysisEntryPoint);
128+
}
126129

127130
void PathDiagnosticConsumer::anchor() {}
128131

clang/lib/StaticAnalyzer/Core/BugReporter.cpp

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@ class PathDiagnosticConstruct {
138138
public:
139139
PathDiagnosticConstruct(const PathDiagnosticConsumer *PDC,
140140
const ExplodedNode *ErrorNode,
141-
const PathSensitiveBugReport *R);
141+
const PathSensitiveBugReport *R,
142+
const Decl *AnalysisEntryPoint);
142143

143144
/// \returns the location context associated with the current position in the
144145
/// bug path.
@@ -1323,24 +1324,26 @@ void PathDiagnosticBuilder::generatePathDiagnosticsForNode(
13231324
}
13241325

13251326
static std::unique_ptr<PathDiagnostic>
1326-
generateDiagnosticForBasicReport(const BasicBugReport *R) {
1327+
generateDiagnosticForBasicReport(const BasicBugReport *R,
1328+
const Decl *AnalysisEntryPoint) {
13271329
const BugType &BT = R->getBugType();
13281330
return std::make_unique<PathDiagnostic>(
13291331
BT.getCheckerName(), R->getDeclWithIssue(), BT.getDescription(),
13301332
R->getDescription(), R->getShortDescription(/*UseFallback=*/false),
13311333
BT.getCategory(), R->getUniqueingLocation(), R->getUniqueingDecl(),
1332-
std::make_unique<FilesToLineNumsMap>());
1334+
AnalysisEntryPoint, std::make_unique<FilesToLineNumsMap>());
13331335
}
13341336

13351337
static std::unique_ptr<PathDiagnostic>
13361338
generateEmptyDiagnosticForReport(const PathSensitiveBugReport *R,
1337-
const SourceManager &SM) {
1339+
const SourceManager &SM,
1340+
const Decl *AnalysisEntryPoint) {
13381341
const BugType &BT = R->getBugType();
13391342
return std::make_unique<PathDiagnostic>(
13401343
BT.getCheckerName(), R->getDeclWithIssue(), BT.getDescription(),
13411344
R->getDescription(), R->getShortDescription(/*UseFallback=*/false),
13421345
BT.getCategory(), R->getUniqueingLocation(), R->getUniqueingDecl(),
1343-
findExecutedLines(SM, R->getErrorNode()));
1346+
AnalysisEntryPoint, findExecutedLines(SM, R->getErrorNode()));
13441347
}
13451348

13461349
static const Stmt *getStmtParent(const Stmt *S, const ParentMap &PM) {
@@ -1976,10 +1979,11 @@ static void updateExecutedLinesWithDiagnosticPieces(PathDiagnostic &PD) {
19761979

19771980
PathDiagnosticConstruct::PathDiagnosticConstruct(
19781981
const PathDiagnosticConsumer *PDC, const ExplodedNode *ErrorNode,
1979-
const PathSensitiveBugReport *R)
1982+
const PathSensitiveBugReport *R, const Decl *AnalysisEntryPoint)
19801983
: Consumer(PDC), CurrentNode(ErrorNode),
19811984
SM(CurrentNode->getCodeDecl().getASTContext().getSourceManager()),
1982-
PD(generateEmptyDiagnosticForReport(R, getSourceManager())) {
1985+
PD(generateEmptyDiagnosticForReport(R, getSourceManager(),
1986+
AnalysisEntryPoint)) {
19831987
LCM[&PD->getActivePath()] = ErrorNode->getLocationContext();
19841988
}
19851989

@@ -1993,13 +1997,14 @@ PathDiagnosticBuilder::PathDiagnosticBuilder(
19931997

19941998
std::unique_ptr<PathDiagnostic>
19951999
PathDiagnosticBuilder::generate(const PathDiagnosticConsumer *PDC) const {
1996-
PathDiagnosticConstruct Construct(PDC, ErrorNode, R);
2000+
const Decl *EntryPoint = getBugReporter().getAnalysisEntryPoint();
2001+
PathDiagnosticConstruct Construct(PDC, ErrorNode, R, EntryPoint);
19972002

19982003
const SourceManager &SM = getSourceManager();
19992004
const AnalyzerOptions &Opts = getAnalyzerOptions();
20002005

20012006
if (!PDC->shouldGenerateDiagnostics())
2002-
return generateEmptyDiagnosticForReport(R, getSourceManager());
2007+
return generateEmptyDiagnosticForReport(R, getSourceManager(), EntryPoint);
20032008

20042009
// Construct the final (warning) event for the bug report.
20052010
auto EndNotes = VisitorsDiagnostics->find(ErrorNode);
@@ -3123,6 +3128,16 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) {
31233128
Pieces.back()->addFixit(I);
31243129

31253130
updateExecutedLinesWithDiagnosticPieces(*PD);
3131+
3132+
// If we are debugging, let's have the entry point as the first note.
3133+
if (getAnalyzerOptions().AnalyzerDisplayProgress ||
3134+
getAnalyzerOptions().AnalyzerNoteAnalysisEntryPoints) {
3135+
const Decl *EntryPoint = getAnalysisEntryPoint();
3136+
Pieces.push_front(std::make_shared<PathDiagnosticEventPiece>(
3137+
PathDiagnosticLocation{EntryPoint->getLocation(), getSourceManager()},
3138+
"[debug] analyzing from " +
3139+
AnalysisDeclContext::getFunctionName(EntryPoint)));
3140+
}
31263141
Consumer->HandlePathDiagnostic(std::move(PD));
31273142
}
31283143
}
@@ -3211,7 +3226,8 @@ BugReporter::generateDiagnosticForConsumerMap(
32113226
auto *basicReport = cast<BasicBugReport>(exampleReport);
32123227
auto Out = std::make_unique<DiagnosticForConsumerMapTy>();
32133228
for (auto *Consumer : consumers)
3214-
(*Out)[Consumer] = generateDiagnosticForBasicReport(basicReport);
3229+
(*Out)[Consumer] =
3230+
generateDiagnosticForBasicReport(basicReport, AnalysisEntryPoint);
32153231
return Out;
32163232
}
32173233

clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,8 @@ static void reportAnalyzerFunctionMisuse(const AnalyzerOptions &Opts,
527527

528528
void AnalysisConsumer::runAnalysisOnTranslationUnit(ASTContext &C) {
529529
BugReporter BR(*Mgr);
530-
TranslationUnitDecl *TU = C.getTranslationUnitDecl();
530+
const TranslationUnitDecl *TU = C.getTranslationUnitDecl();
531+
BR.setAnalysisEntryPoint(TU);
531532
if (SyntaxCheckTimer)
532533
SyntaxCheckTimer->startTimer();
533534
checkerMgr->runCheckersOnASTDecl(TU, *Mgr, BR);
@@ -675,6 +676,7 @@ void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode,
675676

676677
DisplayFunction(D, Mode, IMode);
677678
BugReporter BR(*Mgr);
679+
BR.setAnalysisEntryPoint(D);
678680

679681
if (Mode & AM_Syntax) {
680682
llvm::TimeRecord CheckerStartTime;

clang/test/Analysis/analyzer-display-progress.cpp

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,46 @@
1-
// RUN: %clang_analyze_cc1 -analyzer-display-progress %s 2>&1 | FileCheck %s
1+
// RUN: %clang_analyze_cc1 -verify %s 2>&1 \
2+
// RUN: -analyzer-display-progress \
3+
// RUN: -analyzer-checker=debug.ExprInspection \
4+
// RUN: -analyzer-output=text \
5+
// RUN: | FileCheck %s
26

3-
void f() {};
4-
void g() {};
5-
void h() {}
7+
void clang_analyzer_warnIfReached();
8+
9+
// expected-note@+2 {{[debug] analyzing from f()}}
10+
// expected-warning@+1 {{REACHABLE}} expected-note@+1 {{REACHABLE}}
11+
void f() { clang_analyzer_warnIfReached(); }
12+
13+
// expected-note@+2 {{[debug] analyzing from g()}}
14+
// expected-warning@+1 {{REACHABLE}} expected-note@+1 {{REACHABLE}}
15+
void g() { clang_analyzer_warnIfReached(); }
16+
17+
// expected-note@+2 {{[debug] analyzing from h()}}
18+
// expected-warning@+1 {{REACHABLE}} expected-note@+1 {{REACHABLE}}
19+
void h() { clang_analyzer_warnIfReached(); }
620

721
struct SomeStruct {
8-
void f() {}
22+
// expected-note@+2 {{[debug] analyzing from SomeStruct::f()}}
23+
// expected-warning@+1 {{REACHABLE}} expected-note@+1 {{REACHABLE}}
24+
void f() { clang_analyzer_warnIfReached(); }
925
};
1026

1127
struct SomeOtherStruct {
12-
void f() {}
28+
// expected-note@+2 {{[debug] analyzing from SomeOtherStruct::f()}}
29+
// expected-warning@+1 {{REACHABLE}} expected-note@+1 {{REACHABLE}}
30+
void f() { clang_analyzer_warnIfReached(); }
1331
};
1432

1533
namespace ns {
1634
struct SomeStruct {
17-
void f(int) {}
18-
void f(float, ::SomeStruct) {}
19-
void f(float, SomeStruct) {}
35+
// expected-note@+2 {{[debug] analyzing from ns::SomeStruct::f(int)}}
36+
// expected-warning@+1 {{REACHABLE}} expected-note@+1 {{REACHABLE}}
37+
void f(int) { clang_analyzer_warnIfReached(); }
38+
// expected-note@+2 {{[debug] analyzing from ns::SomeStruct::f(float, ::SomeStruct)}}
39+
// expected-warning@+1 {{REACHABLE}} expected-note@+1 {{REACHABLE}}
40+
void f(float, ::SomeStruct) { clang_analyzer_warnIfReached(); }
41+
// expected-note@+2 {{[debug] analyzing from ns::SomeStruct::f(float, SomeStruct)}}
42+
// expected-warning@+1 {{REACHABLE}} expected-note@+1 {{REACHABLE}}
43+
void f(float, SomeStruct) { clang_analyzer_warnIfReached(); }
2044
};
2145
}
2246

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,43 @@
1-
// RUN: %clang_analyze_cc1 -fblocks -analyzer-display-progress %s 2>&1 | FileCheck %s
1+
// RUN: %clang_analyze_cc1 -fblocks -verify %s 2>&1 \
2+
// RUN: -analyzer-display-progress \
3+
// RUN: -analyzer-checker=debug.ExprInspection \
4+
// RUN: -analyzer-output=text \
5+
// RUN: | FileCheck %s
26

37
#include "Inputs/system-header-simulator-objc.h"
48

5-
static void f(void) {}
9+
void clang_analyzer_warnIfReached();
10+
11+
// expected-note@+2 {{[debug] analyzing from f}}
12+
// expected-warning@+1 {{REACHABLE}} expected-note@+1 {{REACHABLE}}
13+
static void f(void) { clang_analyzer_warnIfReached(); }
614

715
@interface I: NSObject
816
-(void)instanceMethod:(int)arg1 with:(int)arg2;
917
+(void)classMethod;
1018
@end
1119

1220
@implementation I
13-
-(void)instanceMethod:(int)arg1 with:(int)arg2 {}
14-
+(void)classMethod {}
21+
// expected-warning@+1 {{REACHABLE}} expected-note@+1 {{REACHABLE}}
22+
-(void)instanceMethod:(int)arg1 with:(int)arg2 { clang_analyzer_warnIfReached(); }
23+
24+
// expected-warning@+1 {{REACHABLE}} expected-note@+1 {{REACHABLE}}
25+
+(void)classMethod { clang_analyzer_warnIfReached(); }
1526
@end
1627

28+
// expected-note@+1 3 {{[debug] analyzing from g}}
1729
void g(I *i, int x, int y) {
18-
[I classMethod];
19-
[i instanceMethod: x with: y];
30+
[I classMethod]; // expected-note {{Calling 'classMethod'}}
31+
[i instanceMethod: x with: y]; // expected-note {{Calling 'instanceMethod:with:'}}
2032

2133
void (^block)(void);
22-
block = ^{};
23-
block();
34+
// expected-warning@+1 {{REACHABLE}} expected-note@+1 {{REACHABLE}}
35+
block = ^{ clang_analyzer_warnIfReached(); };
36+
block(); // expected-note {{Calling anonymous block}}
2437
}
2538

2639
// CHECK: analyzer-display-progress.m f
2740
// CHECK: analyzer-display-progress.m -[I instanceMethod:with:]
2841
// CHECK: analyzer-display-progress.m +[I classMethod]
2942
// CHECK: analyzer-display-progress.m g
30-
// CHECK: analyzer-display-progress.m block (line: 22, col: 11)
43+
// CHECK: analyzer-display-progress.m block (line: 35, col: 11)

0 commit comments

Comments
 (0)