1 | //===- CheckerManager.cpp - Static Analyzer Checker Manager ---------------===// |
---|---|
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // Defines the Static Analyzer Checker Manager. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
14 | #include "clang/AST/DeclBase.h" |
15 | #include "clang/AST/Stmt.h" |
16 | #include "clang/Analysis/ProgramPoint.h" |
17 | #include "clang/Basic/JsonSupport.h" |
18 | #include "clang/Basic/LLVM.h" |
19 | #include "clang/Driver/DriverDiagnostic.h" |
20 | #include "clang/StaticAnalyzer/Core/Checker.h" |
21 | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
22 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
23 | #include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h" |
24 | #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" |
25 | #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" |
26 | #include "llvm/ADT/SmallVector.h" |
27 | #include "llvm/Support/ErrorHandling.h" |
28 | #include "llvm/Support/FormatVariadic.h" |
29 | #include "llvm/Support/TimeProfiler.h" |
30 | #include <cassert> |
31 | #include <optional> |
32 | #include <vector> |
33 | |
34 | using namespace clang; |
35 | using namespace ento; |
36 | |
37 | bool CheckerManager::hasPathSensitiveCheckers() const { |
38 | const auto IfAnyAreNonEmpty = [](const auto &... Callbacks) -> bool { |
39 | return (!Callbacks.empty() || ...); |
40 | }; |
41 | return IfAnyAreNonEmpty( |
42 | StmtCheckers, PreObjCMessageCheckers, ObjCMessageNilCheckers, |
43 | PostObjCMessageCheckers, PreCallCheckers, PostCallCheckers, |
44 | LocationCheckers, BindCheckers, BlockEntranceCheckers, |
45 | EndAnalysisCheckers, BeginFunctionCheckers, EndFunctionCheckers, |
46 | BranchConditionCheckers, NewAllocatorCheckers, LiveSymbolsCheckers, |
47 | DeadSymbolsCheckers, RegionChangesCheckers, PointerEscapeCheckers, |
48 | EvalAssumeCheckers, EvalCallCheckers, EndOfTranslationUnitCheckers); |
49 | } |
50 | |
51 | void CheckerManager::reportInvalidCheckerOptionValue( |
52 | const CheckerFrontend *Checker, StringRef OptionName, |
53 | StringRef ExpectedValueDesc) const { |
54 | |
55 | getDiagnostics().Report(diag::err_analyzer_checker_option_invalid_input) |
56 | << (llvm::Twine(Checker->getName()) + ":"+ OptionName).str() |
57 | << ExpectedValueDesc; |
58 | } |
59 | |
60 | //===----------------------------------------------------------------------===// |
61 | // Functions for running checkers for AST traversing.. |
62 | //===----------------------------------------------------------------------===// |
63 | |
64 | void CheckerManager::runCheckersOnASTDecl(const Decl *D, AnalysisManager& mgr, |
65 | BugReporter &BR) { |
66 | assert(D); |
67 | |
68 | unsigned DeclKind = D->getKind(); |
69 | auto [CCI, Inserted] = CachedDeclCheckersMap.try_emplace(Key: DeclKind); |
70 | CachedDeclCheckers *checkers = &(CCI->second); |
71 | if (Inserted) { |
72 | // Find the checkers that should run for this Decl and cache them. |
73 | for (const auto &info : DeclCheckers) |
74 | if (info.IsForDeclFn(D)) |
75 | checkers->push_back(Elt: info.CheckFn); |
76 | } |
77 | |
78 | assert(checkers); |
79 | for (const auto &checker : *checkers) |
80 | checker(D, mgr, BR); |
81 | } |
82 | |
83 | void CheckerManager::runCheckersOnASTBody(const Decl *D, AnalysisManager& mgr, |
84 | BugReporter &BR) { |
85 | assert(D && D->hasBody()); |
86 | |
87 | for (const auto &BodyChecker : BodyCheckers) |
88 | BodyChecker(D, mgr, BR); |
89 | } |
90 | |
91 | //===----------------------------------------------------------------------===// |
92 | // Functions for running checkers for path-sensitive checking. |
93 | //===----------------------------------------------------------------------===// |
94 | |
95 | template <typename CHECK_CTX> |
96 | static void expandGraphWithCheckers(CHECK_CTX checkCtx, |
97 | ExplodedNodeSet &Dst, |
98 | const ExplodedNodeSet &Src) { |
99 | const NodeBuilderContext &BldrCtx = checkCtx.Eng.getBuilderContext(); |
100 | if (Src.empty()) |
101 | return; |
102 | |
103 | typename CHECK_CTX::CheckersTy::const_iterator |
104 | I = checkCtx.checkers_begin(), E = checkCtx.checkers_end(); |
105 | if (I == E) { |
106 | Dst.insert(S: Src); |
107 | return; |
108 | } |
109 | |
110 | ExplodedNodeSet Tmp1, Tmp2; |
111 | const ExplodedNodeSet *PrevSet = &Src; |
112 | |
113 | for (; I != E; ++I) { |
114 | ExplodedNodeSet *CurrSet = nullptr; |
115 | if (I+1 == E) |
116 | CurrSet = &Dst; |
117 | else { |
118 | CurrSet = (PrevSet == &Tmp1) ? &Tmp2 : &Tmp1; |
119 | CurrSet->clear(); |
120 | } |
121 | |
122 | NodeBuilder B(*PrevSet, *CurrSet, BldrCtx); |
123 | for (const auto &NI : *PrevSet) |
124 | checkCtx.runChecker(*I, B, NI); |
125 | |
126 | // If all the produced transitions are sinks, stop. |
127 | if (CurrSet->empty()) |
128 | return; |
129 | |
130 | // Update which NodeSet is the current one. |
131 | PrevSet = CurrSet; |
132 | } |
133 | } |
134 | |
135 | namespace { |
136 | |
137 | std::string checkerScopeName(StringRef Name, const CheckerBackend *Checker) { |
138 | if (!llvm::timeTraceProfilerEnabled()) |
139 | return ""; |
140 | StringRef CheckerTag = Checker ? Checker->getDebugTag() : "<unknown>"; |
141 | return (Name + ":"+ CheckerTag).str(); |
142 | } |
143 | |
144 | struct CheckStmtContext { |
145 | using CheckersTy = SmallVectorImpl<CheckerManager::CheckStmtFunc>; |
146 | |
147 | bool IsPreVisit; |
148 | const CheckersTy &Checkers; |
149 | const Stmt *S; |
150 | ExprEngine &Eng; |
151 | bool WasInlined; |
152 | |
153 | CheckStmtContext(bool isPreVisit, const CheckersTy &checkers, |
154 | const Stmt *s, ExprEngine &eng, bool wasInlined = false) |
155 | : IsPreVisit(isPreVisit), Checkers(checkers), S(s), Eng(eng), |
156 | WasInlined(wasInlined) {} |
157 | |
158 | CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } |
159 | CheckersTy::const_iterator checkers_end() { return Checkers.end(); } |
160 | |
161 | void runChecker(CheckerManager::CheckStmtFunc checkFn, |
162 | NodeBuilder &Bldr, ExplodedNode *Pred) { |
163 | llvm::TimeTraceScope TimeScope(checkerScopeName(Name: "Stmt", Checker: checkFn.Checker)); |
164 | // FIXME: Remove respondsToCallback from CheckerContext; |
165 | ProgramPoint::Kind K = IsPreVisit ? ProgramPoint::PreStmtKind : |
166 | ProgramPoint::PostStmtKind; |
167 | const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, |
168 | LC: Pred->getLocationContext(), tag: checkFn.Checker); |
169 | CheckerContext C(Bldr, Eng, Pred, L, WasInlined); |
170 | checkFn(S, C); |
171 | } |
172 | }; |
173 | |
174 | } // namespace |
175 | |
176 | /// Run checkers for visiting Stmts. |
177 | void CheckerManager::runCheckersForStmt(bool isPreVisit, |
178 | ExplodedNodeSet &Dst, |
179 | const ExplodedNodeSet &Src, |
180 | const Stmt *S, |
181 | ExprEngine &Eng, |
182 | bool WasInlined) { |
183 | CheckStmtContext C(isPreVisit, getCachedStmtCheckersFor(S, isPreVisit), |
184 | S, Eng, WasInlined); |
185 | llvm::TimeTraceScope TimeScope( |
186 | isPreVisit ? "CheckerManager::runCheckersForStmt (Pre)" |
187 | : "CheckerManager::runCheckersForStmt (Post)"); |
188 | expandGraphWithCheckers(checkCtx: C, Dst, Src); |
189 | } |
190 | |
191 | namespace { |
192 | |
193 | struct CheckObjCMessageContext { |
194 | using CheckersTy = std::vector<CheckerManager::CheckObjCMessageFunc>; |
195 | |
196 | ObjCMessageVisitKind Kind; |
197 | bool WasInlined; |
198 | const CheckersTy &Checkers; |
199 | const ObjCMethodCall &Msg; |
200 | ExprEngine &Eng; |
201 | |
202 | CheckObjCMessageContext(ObjCMessageVisitKind visitKind, |
203 | const CheckersTy &checkers, |
204 | const ObjCMethodCall &msg, ExprEngine &eng, |
205 | bool wasInlined) |
206 | : Kind(visitKind), WasInlined(wasInlined), Checkers(checkers), Msg(msg), |
207 | Eng(eng) {} |
208 | |
209 | CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } |
210 | CheckersTy::const_iterator checkers_end() { return Checkers.end(); } |
211 | |
212 | void runChecker(CheckerManager::CheckObjCMessageFunc checkFn, |
213 | NodeBuilder &Bldr, ExplodedNode *Pred) { |
214 | llvm::TimeTraceScope TimeScope( |
215 | checkerScopeName(Name: "ObjCMsg", Checker: checkFn.Checker)); |
216 | bool IsPreVisit; |
217 | |
218 | switch (Kind) { |
219 | case ObjCMessageVisitKind::Pre: |
220 | IsPreVisit = true; |
221 | break; |
222 | case ObjCMessageVisitKind::MessageNil: |
223 | case ObjCMessageVisitKind::Post: |
224 | IsPreVisit = false; |
225 | break; |
226 | } |
227 | |
228 | const ProgramPoint &L = Msg.getProgramPoint(IsPreVisit,Tag: checkFn.Checker); |
229 | CheckerContext C(Bldr, Eng, Pred, L, WasInlined); |
230 | |
231 | checkFn(*Msg.cloneWithState<ObjCMethodCall>(NewState: Pred->getState()), C); |
232 | } |
233 | }; |
234 | |
235 | } // namespace |
236 | |
237 | /// Run checkers for visiting obj-c messages. |
238 | void CheckerManager::runCheckersForObjCMessage(ObjCMessageVisitKind visitKind, |
239 | ExplodedNodeSet &Dst, |
240 | const ExplodedNodeSet &Src, |
241 | const ObjCMethodCall &msg, |
242 | ExprEngine &Eng, |
243 | bool WasInlined) { |
244 | const auto &checkers = getObjCMessageCheckers(Kind: visitKind); |
245 | CheckObjCMessageContext C(visitKind, checkers, msg, Eng, WasInlined); |
246 | llvm::TimeTraceScope TimeScope("CheckerManager::runCheckersForObjCMessage"); |
247 | expandGraphWithCheckers(checkCtx: C, Dst, Src); |
248 | } |
249 | |
250 | const std::vector<CheckerManager::CheckObjCMessageFunc> & |
251 | CheckerManager::getObjCMessageCheckers(ObjCMessageVisitKind Kind) const { |
252 | switch (Kind) { |
253 | case ObjCMessageVisitKind::Pre: |
254 | return PreObjCMessageCheckers; |
255 | break; |
256 | case ObjCMessageVisitKind::Post: |
257 | return PostObjCMessageCheckers; |
258 | case ObjCMessageVisitKind::MessageNil: |
259 | return ObjCMessageNilCheckers; |
260 | } |
261 | llvm_unreachable("Unknown Kind"); |
262 | } |
263 | |
264 | namespace { |
265 | |
266 | // FIXME: This has all the same signatures as CheckObjCMessageContext. |
267 | // Is there a way we can merge the two? |
268 | struct CheckCallContext { |
269 | using CheckersTy = std::vector<CheckerManager::CheckCallFunc>; |
270 | |
271 | bool IsPreVisit, WasInlined; |
272 | const CheckersTy &Checkers; |
273 | const CallEvent &Call; |
274 | ExprEngine &Eng; |
275 | |
276 | CheckCallContext(bool isPreVisit, const CheckersTy &checkers, |
277 | const CallEvent &call, ExprEngine &eng, |
278 | bool wasInlined) |
279 | : IsPreVisit(isPreVisit), WasInlined(wasInlined), Checkers(checkers), |
280 | Call(call), Eng(eng) {} |
281 | |
282 | CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } |
283 | CheckersTy::const_iterator checkers_end() { return Checkers.end(); } |
284 | |
285 | void runChecker(CheckerManager::CheckCallFunc checkFn, |
286 | NodeBuilder &Bldr, ExplodedNode *Pred) { |
287 | llvm::TimeTraceScope TimeScope(checkerScopeName(Name: "Call", Checker: checkFn.Checker)); |
288 | const ProgramPoint &L = Call.getProgramPoint(IsPreVisit,Tag: checkFn.Checker); |
289 | CheckerContext C(Bldr, Eng, Pred, L, WasInlined); |
290 | |
291 | checkFn(*Call.cloneWithState(NewState: Pred->getState()), C); |
292 | } |
293 | }; |
294 | |
295 | } // namespace |
296 | |
297 | /// Run checkers for visiting an abstract call event. |
298 | void CheckerManager::runCheckersForCallEvent(bool isPreVisit, |
299 | ExplodedNodeSet &Dst, |
300 | const ExplodedNodeSet &Src, |
301 | const CallEvent &Call, |
302 | ExprEngine &Eng, |
303 | bool WasInlined) { |
304 | CheckCallContext C(isPreVisit, |
305 | isPreVisit ? PreCallCheckers |
306 | : PostCallCheckers, |
307 | Call, Eng, WasInlined); |
308 | llvm::TimeTraceScope TimeScope( |
309 | isPreVisit ? "CheckerManager::runCheckersForCallEvent (Pre)" |
310 | : "CheckerManager::runCheckersForCallEvent (Post)"); |
311 | expandGraphWithCheckers(checkCtx: C, Dst, Src); |
312 | } |
313 | |
314 | namespace { |
315 | |
316 | struct CheckLocationContext { |
317 | using CheckersTy = std::vector<CheckerManager::CheckLocationFunc>; |
318 | |
319 | const CheckersTy &Checkers; |
320 | SVal Loc; |
321 | bool IsLoad; |
322 | const Stmt *NodeEx; /* Will become a CFGStmt */ |
323 | const Stmt *BoundEx; |
324 | ExprEngine &Eng; |
325 | |
326 | CheckLocationContext(const CheckersTy &checkers, |
327 | SVal loc, bool isLoad, const Stmt *NodeEx, |
328 | const Stmt *BoundEx, |
329 | ExprEngine &eng) |
330 | : Checkers(checkers), Loc(loc), IsLoad(isLoad), NodeEx(NodeEx), |
331 | BoundEx(BoundEx), Eng(eng) {} |
332 | |
333 | CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } |
334 | CheckersTy::const_iterator checkers_end() { return Checkers.end(); } |
335 | |
336 | void runChecker(CheckerManager::CheckLocationFunc checkFn, |
337 | NodeBuilder &Bldr, ExplodedNode *Pred) { |
338 | llvm::TimeTraceScope TimeScope(checkerScopeName(Name: "Loc", Checker: checkFn.Checker)); |
339 | ProgramPoint::Kind K = IsLoad ? ProgramPoint::PreLoadKind : |
340 | ProgramPoint::PreStoreKind; |
341 | const ProgramPoint &L = |
342 | ProgramPoint::getProgramPoint(S: NodeEx, K, |
343 | LC: Pred->getLocationContext(), |
344 | tag: checkFn.Checker); |
345 | CheckerContext C(Bldr, Eng, Pred, L); |
346 | checkFn(Loc, IsLoad, BoundEx, C); |
347 | } |
348 | }; |
349 | |
350 | } // namespace |
351 | |
352 | /// Run checkers for load/store of a location. |
353 | |
354 | void CheckerManager::runCheckersForLocation(ExplodedNodeSet &Dst, |
355 | const ExplodedNodeSet &Src, |
356 | SVal location, bool isLoad, |
357 | const Stmt *NodeEx, |
358 | const Stmt *BoundEx, |
359 | ExprEngine &Eng) { |
360 | CheckLocationContext C(LocationCheckers, location, isLoad, NodeEx, |
361 | BoundEx, Eng); |
362 | llvm::TimeTraceScope TimeScope( |
363 | isLoad ? "CheckerManager::runCheckersForLocation (Load)" |
364 | : "CheckerManager::runCheckersForLocation (Store)"); |
365 | expandGraphWithCheckers(checkCtx: C, Dst, Src); |
366 | } |
367 | |
368 | namespace { |
369 | |
370 | struct CheckBindContext { |
371 | using CheckersTy = std::vector<CheckerManager::CheckBindFunc>; |
372 | |
373 | const CheckersTy &Checkers; |
374 | SVal Loc; |
375 | SVal Val; |
376 | const Stmt *S; |
377 | ExprEngine &Eng; |
378 | const ProgramPoint &PP; |
379 | |
380 | CheckBindContext(const CheckersTy &checkers, |
381 | SVal loc, SVal val, const Stmt *s, ExprEngine &eng, |
382 | const ProgramPoint &pp) |
383 | : Checkers(checkers), Loc(loc), Val(val), S(s), Eng(eng), PP(pp) {} |
384 | |
385 | CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } |
386 | CheckersTy::const_iterator checkers_end() { return Checkers.end(); } |
387 | |
388 | void runChecker(CheckerManager::CheckBindFunc checkFn, |
389 | NodeBuilder &Bldr, ExplodedNode *Pred) { |
390 | llvm::TimeTraceScope TimeScope(checkerScopeName(Name: "Bind", Checker: checkFn.Checker)); |
391 | const ProgramPoint &L = PP.withTag(tag: checkFn.Checker); |
392 | CheckerContext C(Bldr, Eng, Pred, L); |
393 | |
394 | checkFn(Loc, Val, S, C); |
395 | } |
396 | }; |
397 | |
398 | llvm::TimeTraceMetadata getTimeTraceBindMetadata(SVal Val) { |
399 | assert(llvm::timeTraceProfilerEnabled()); |
400 | std::string Name; |
401 | llvm::raw_string_ostream OS(Name); |
402 | Val.dumpToStream(OS); |
403 | return llvm::TimeTraceMetadata{.Detail: OS.str(), .File: ""}; |
404 | } |
405 | |
406 | } // namespace |
407 | |
408 | /// Run checkers for binding of a value to a location. |
409 | void CheckerManager::runCheckersForBind(ExplodedNodeSet &Dst, |
410 | const ExplodedNodeSet &Src, |
411 | SVal location, SVal val, |
412 | const Stmt *S, ExprEngine &Eng, |
413 | const ProgramPoint &PP) { |
414 | CheckBindContext C(BindCheckers, location, val, S, Eng, PP); |
415 | llvm::TimeTraceScope TimeScope{ |
416 | "CheckerManager::runCheckersForBind", |
417 | [&val]() { return getTimeTraceBindMetadata(Val: val); }}; |
418 | expandGraphWithCheckers(checkCtx: C, Dst, Src); |
419 | } |
420 | |
421 | namespace { |
422 | struct CheckBlockEntranceContext { |
423 | using CheckBlockEntranceFunc = CheckerManager::CheckBlockEntranceFunc; |
424 | using CheckersTy = std::vector<CheckBlockEntranceFunc>; |
425 | |
426 | const CheckersTy &Checkers; |
427 | const BlockEntrance &Entrance; |
428 | ExprEngine &Eng; |
429 | |
430 | CheckBlockEntranceContext(const CheckersTy &Checkers, |
431 | const BlockEntrance &Entrance, ExprEngine &Eng) |
432 | : Checkers(Checkers), Entrance(Entrance), Eng(Eng) {} |
433 | |
434 | auto checkers_begin() const { return Checkers.begin(); } |
435 | auto checkers_end() const { return Checkers.end(); } |
436 | |
437 | void runChecker(CheckBlockEntranceFunc CheckFn, NodeBuilder &Bldr, |
438 | ExplodedNode *Pred) { |
439 | llvm::TimeTraceScope TimeScope( |
440 | checkerScopeName(Name: "BlockEntrance", Checker: CheckFn.Checker)); |
441 | CheckerContext C(Bldr, Eng, Pred, Entrance.withTag(tag: CheckFn.Checker)); |
442 | CheckFn(Entrance, C); |
443 | } |
444 | }; |
445 | |
446 | } // namespace |
447 | |
448 | void CheckerManager::runCheckersForBlockEntrance(ExplodedNodeSet &Dst, |
449 | const ExplodedNodeSet &Src, |
450 | const BlockEntrance &Entrance, |
451 | ExprEngine &Eng) const { |
452 | CheckBlockEntranceContext C(BlockEntranceCheckers, Entrance, Eng); |
453 | llvm::TimeTraceScope TimeScope{"CheckerManager::runCheckersForBlockEntrance"}; |
454 | expandGraphWithCheckers(checkCtx: C, Dst, Src); |
455 | } |
456 | |
457 | void CheckerManager::runCheckersForEndAnalysis(ExplodedGraph &G, |
458 | BugReporter &BR, |
459 | ExprEngine &Eng) { |
460 | for (const auto &EndAnalysisChecker : EndAnalysisCheckers) |
461 | EndAnalysisChecker(G, BR, Eng); |
462 | } |
463 | |
464 | namespace { |
465 | |
466 | struct CheckBeginFunctionContext { |
467 | using CheckersTy = std::vector<CheckerManager::CheckBeginFunctionFunc>; |
468 | |
469 | const CheckersTy &Checkers; |
470 | ExprEngine &Eng; |
471 | const ProgramPoint &PP; |
472 | |
473 | CheckBeginFunctionContext(const CheckersTy &Checkers, ExprEngine &Eng, |
474 | const ProgramPoint &PP) |
475 | : Checkers(Checkers), Eng(Eng), PP(PP) {} |
476 | |
477 | CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } |
478 | CheckersTy::const_iterator checkers_end() { return Checkers.end(); } |
479 | |
480 | void runChecker(CheckerManager::CheckBeginFunctionFunc checkFn, |
481 | NodeBuilder &Bldr, ExplodedNode *Pred) { |
482 | llvm::TimeTraceScope TimeScope(checkerScopeName(Name: "Begin", Checker: checkFn.Checker)); |
483 | const ProgramPoint &L = PP.withTag(tag: checkFn.Checker); |
484 | CheckerContext C(Bldr, Eng, Pred, L); |
485 | |
486 | checkFn(C); |
487 | } |
488 | }; |
489 | |
490 | } // namespace |
491 | |
492 | void CheckerManager::runCheckersForBeginFunction(ExplodedNodeSet &Dst, |
493 | const BlockEdge &L, |
494 | ExplodedNode *Pred, |
495 | ExprEngine &Eng) { |
496 | ExplodedNodeSet Src; |
497 | Src.insert(S: Pred); |
498 | CheckBeginFunctionContext C(BeginFunctionCheckers, Eng, L); |
499 | llvm::TimeTraceScope TimeScope("CheckerManager::runCheckersForBeginFunction"); |
500 | expandGraphWithCheckers(checkCtx: C, Dst, Src); |
501 | } |
502 | |
503 | /// Run checkers for end of path. |
504 | // Note, We do not chain the checker output (like in expandGraphWithCheckers) |
505 | // for this callback since end of path nodes are expected to be final. |
506 | void CheckerManager::runCheckersForEndFunction(NodeBuilderContext &BC, |
507 | ExplodedNodeSet &Dst, |
508 | ExplodedNode *Pred, |
509 | ExprEngine &Eng, |
510 | const ReturnStmt *RS) { |
511 | // We define the builder outside of the loop because if at least one checker |
512 | // creates a successor for Pred, we do not need to generate an |
513 | // autotransition for it. |
514 | NodeBuilder Bldr(Pred, Dst, BC); |
515 | for (const auto &checkFn : EndFunctionCheckers) { |
516 | const ProgramPoint &L = |
517 | FunctionExitPoint(RS, Pred->getLocationContext(), checkFn.Checker); |
518 | CheckerContext C(Bldr, Eng, Pred, L); |
519 | llvm::TimeTraceScope TimeScope(checkerScopeName(Name: "End", Checker: checkFn.Checker)); |
520 | checkFn(RS, C); |
521 | } |
522 | } |
523 | |
524 | namespace { |
525 | |
526 | struct CheckBranchConditionContext { |
527 | using CheckersTy = std::vector<CheckerManager::CheckBranchConditionFunc>; |
528 | |
529 | const CheckersTy &Checkers; |
530 | const Stmt *Condition; |
531 | ExprEngine &Eng; |
532 | |
533 | CheckBranchConditionContext(const CheckersTy &checkers, |
534 | const Stmt *Cond, ExprEngine &eng) |
535 | : Checkers(checkers), Condition(Cond), Eng(eng) {} |
536 | |
537 | CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } |
538 | CheckersTy::const_iterator checkers_end() { return Checkers.end(); } |
539 | |
540 | void runChecker(CheckerManager::CheckBranchConditionFunc checkFn, |
541 | NodeBuilder &Bldr, ExplodedNode *Pred) { |
542 | llvm::TimeTraceScope TimeScope( |
543 | checkerScopeName(Name: "BranchCond", Checker: checkFn.Checker)); |
544 | ProgramPoint L = PostCondition(Condition, Pred->getLocationContext(), |
545 | checkFn.Checker); |
546 | CheckerContext C(Bldr, Eng, Pred, L); |
547 | checkFn(Condition, C); |
548 | } |
549 | }; |
550 | |
551 | } // namespace |
552 | |
553 | /// Run checkers for branch condition. |
554 | void CheckerManager::runCheckersForBranchCondition(const Stmt *Condition, |
555 | ExplodedNodeSet &Dst, |
556 | ExplodedNode *Pred, |
557 | ExprEngine &Eng) { |
558 | ExplodedNodeSet Src; |
559 | Src.insert(S: Pred); |
560 | CheckBranchConditionContext C(BranchConditionCheckers, Condition, Eng); |
561 | llvm::TimeTraceScope TimeScope( |
562 | "CheckerManager::runCheckersForBranchCondition"); |
563 | expandGraphWithCheckers(checkCtx: C, Dst, Src); |
564 | } |
565 | |
566 | namespace { |
567 | |
568 | struct CheckNewAllocatorContext { |
569 | using CheckersTy = std::vector<CheckerManager::CheckNewAllocatorFunc>; |
570 | |
571 | const CheckersTy &Checkers; |
572 | const CXXAllocatorCall &Call; |
573 | bool WasInlined; |
574 | ExprEngine &Eng; |
575 | |
576 | CheckNewAllocatorContext(const CheckersTy &Checkers, |
577 | const CXXAllocatorCall &Call, bool WasInlined, |
578 | ExprEngine &Eng) |
579 | : Checkers(Checkers), Call(Call), WasInlined(WasInlined), Eng(Eng) {} |
580 | |
581 | CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } |
582 | CheckersTy::const_iterator checkers_end() { return Checkers.end(); } |
583 | |
584 | void runChecker(CheckerManager::CheckNewAllocatorFunc checkFn, |
585 | NodeBuilder &Bldr, ExplodedNode *Pred) { |
586 | llvm::TimeTraceScope TimeScope( |
587 | checkerScopeName(Name: "Allocator", Checker: checkFn.Checker)); |
588 | ProgramPoint L = PostAllocatorCall( |
589 | Call.getOriginExpr(), Pred->getLocationContext(), checkFn.Checker); |
590 | CheckerContext C(Bldr, Eng, Pred, L, WasInlined); |
591 | checkFn(cast<CXXAllocatorCall>(Val: *Call.cloneWithState(NewState: Pred->getState())), |
592 | C); |
593 | } |
594 | }; |
595 | |
596 | } // namespace |
597 | |
598 | void CheckerManager::runCheckersForNewAllocator(const CXXAllocatorCall &Call, |
599 | ExplodedNodeSet &Dst, |
600 | ExplodedNode *Pred, |
601 | ExprEngine &Eng, |
602 | bool WasInlined) { |
603 | ExplodedNodeSet Src; |
604 | Src.insert(S: Pred); |
605 | CheckNewAllocatorContext C(NewAllocatorCheckers, Call, WasInlined, Eng); |
606 | llvm::TimeTraceScope TimeScope("CheckerManager::runCheckersForNewAllocator"); |
607 | expandGraphWithCheckers(checkCtx: C, Dst, Src); |
608 | } |
609 | |
610 | /// Run checkers for live symbols. |
611 | void CheckerManager::runCheckersForLiveSymbols(ProgramStateRef state, |
612 | SymbolReaper &SymReaper) { |
613 | for (const auto &LiveSymbolsChecker : LiveSymbolsCheckers) |
614 | LiveSymbolsChecker(state, SymReaper); |
615 | } |
616 | |
617 | namespace { |
618 | |
619 | struct CheckDeadSymbolsContext { |
620 | using CheckersTy = std::vector<CheckerManager::CheckDeadSymbolsFunc>; |
621 | |
622 | const CheckersTy &Checkers; |
623 | SymbolReaper &SR; |
624 | const Stmt *S; |
625 | ExprEngine &Eng; |
626 | ProgramPoint::Kind ProgarmPointKind; |
627 | |
628 | CheckDeadSymbolsContext(const CheckersTy &checkers, SymbolReaper &sr, |
629 | const Stmt *s, ExprEngine &eng, |
630 | ProgramPoint::Kind K) |
631 | : Checkers(checkers), SR(sr), S(s), Eng(eng), ProgarmPointKind(K) {} |
632 | |
633 | CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } |
634 | CheckersTy::const_iterator checkers_end() { return Checkers.end(); } |
635 | |
636 | void runChecker(CheckerManager::CheckDeadSymbolsFunc checkFn, |
637 | NodeBuilder &Bldr, ExplodedNode *Pred) { |
638 | llvm::TimeTraceScope TimeScope( |
639 | checkerScopeName(Name: "DeadSymbols", Checker: checkFn.Checker)); |
640 | const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K: ProgarmPointKind, |
641 | LC: Pred->getLocationContext(), tag: checkFn.Checker); |
642 | CheckerContext C(Bldr, Eng, Pred, L); |
643 | |
644 | // Note, do not pass the statement to the checkers without letting them |
645 | // differentiate if we ran remove dead bindings before or after the |
646 | // statement. |
647 | checkFn(SR, C); |
648 | } |
649 | }; |
650 | |
651 | } // namespace |
652 | |
653 | /// Run checkers for dead symbols. |
654 | void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst, |
655 | const ExplodedNodeSet &Src, |
656 | SymbolReaper &SymReaper, |
657 | const Stmt *S, |
658 | ExprEngine &Eng, |
659 | ProgramPoint::Kind K) { |
660 | CheckDeadSymbolsContext C(DeadSymbolsCheckers, SymReaper, S, Eng, K); |
661 | llvm::TimeTraceScope TimeScope("CheckerManager::runCheckersForDeadSymbols"); |
662 | expandGraphWithCheckers(checkCtx: C, Dst, Src); |
663 | } |
664 | |
665 | /// Run checkers for region changes. |
666 | ProgramStateRef |
667 | CheckerManager::runCheckersForRegionChanges(ProgramStateRef state, |
668 | const InvalidatedSymbols *invalidated, |
669 | ArrayRef<const MemRegion *> ExplicitRegions, |
670 | ArrayRef<const MemRegion *> Regions, |
671 | const LocationContext *LCtx, |
672 | const CallEvent *Call) { |
673 | for (const auto &RegionChangesChecker : RegionChangesCheckers) { |
674 | // If any checker declares the state infeasible (or if it starts that way), |
675 | // bail out. |
676 | if (!state) |
677 | return nullptr; |
678 | state = RegionChangesChecker(state, invalidated, ExplicitRegions, Regions, |
679 | LCtx, Call); |
680 | } |
681 | return state; |
682 | } |
683 | |
684 | /// Run checkers to process symbol escape event. |
685 | ProgramStateRef |
686 | CheckerManager::runCheckersForPointerEscape(ProgramStateRef State, |
687 | const InvalidatedSymbols &Escaped, |
688 | const CallEvent *Call, |
689 | PointerEscapeKind Kind, |
690 | RegionAndSymbolInvalidationTraits *ETraits) { |
691 | assert((Call != nullptr || |
692 | (Kind != PSK_DirectEscapeOnCall && |
693 | Kind != PSK_IndirectEscapeOnCall)) && |
694 | "Call must not be NULL when escaping on call"); |
695 | for (const auto &PointerEscapeChecker : PointerEscapeCheckers) { |
696 | // If any checker declares the state infeasible (or if it starts that |
697 | // way), bail out. |
698 | if (!State) |
699 | return nullptr; |
700 | State = PointerEscapeChecker(State, Escaped, Call, Kind, ETraits); |
701 | } |
702 | return State; |
703 | } |
704 | |
705 | /// Run checkers for handling assumptions on symbolic values. |
706 | ProgramStateRef |
707 | CheckerManager::runCheckersForEvalAssume(ProgramStateRef state, |
708 | SVal Cond, bool Assumption) { |
709 | for (const auto &EvalAssumeChecker : EvalAssumeCheckers) { |
710 | // If any checker declares the state infeasible (or if it starts that way), |
711 | // bail out. |
712 | if (!state) |
713 | return nullptr; |
714 | state = EvalAssumeChecker(state, Cond, Assumption); |
715 | } |
716 | return state; |
717 | } |
718 | |
719 | /// Run checkers for evaluating a call. |
720 | /// Only one checker will evaluate the call. |
721 | void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, |
722 | const ExplodedNodeSet &Src, |
723 | const CallEvent &Call, |
724 | ExprEngine &Eng, |
725 | const EvalCallOptions &CallOpts) { |
726 | for (auto *const Pred : Src) { |
727 | std::optional<StringRef> evaluatorChecker; |
728 | |
729 | ExplodedNodeSet checkDst; |
730 | NodeBuilder B(Pred, checkDst, Eng.getBuilderContext()); |
731 | |
732 | // Check if any of the EvalCall callbacks can evaluate the call. |
733 | for (const auto &EvalCallChecker : EvalCallCheckers) { |
734 | // TODO: Support the situation when the call doesn't correspond |
735 | // to any Expr. |
736 | ProgramPoint L = ProgramPoint::getProgramPoint( |
737 | Call.getOriginExpr(), ProgramPoint::PostStmtKind, |
738 | Pred->getLocationContext(), EvalCallChecker.Checker); |
739 | bool evaluated = false; |
740 | { // CheckerContext generates transitions(populates checkDest) on |
741 | // destruction, so introduce the scope to make sure it gets properly |
742 | // populated. |
743 | CheckerContext C(B, Eng, Pred, L); |
744 | evaluated = EvalCallChecker(Call, C); |
745 | } |
746 | #ifndef NDEBUG |
747 | if (evaluated && evaluatorChecker) { |
748 | const auto toString = [](const CallEvent &Call) -> std::string { |
749 | std::string Buf; |
750 | llvm::raw_string_ostream OS(Buf); |
751 | Call.dump(Out&: OS); |
752 | return Buf; |
753 | }; |
754 | std::string AssertionMessage = llvm::formatv( |
755 | Fmt: "The '{0}' call has been already evaluated by the {1} checker, " |
756 | "while the {2} checker also tried to evaluate the same call. At " |
757 | "most one checker supposed to evaluate a call.", |
758 | Vals: toString(Call), Vals&: evaluatorChecker, |
759 | Vals: EvalCallChecker.Checker->getDebugTag()); |
760 | llvm_unreachable(AssertionMessage.c_str()); |
761 | } |
762 | #endif |
763 | if (evaluated) { |
764 | evaluatorChecker = EvalCallChecker.Checker->getDebugTag(); |
765 | Dst.insert(S: checkDst); |
766 | #ifdef NDEBUG |
767 | break; // on release don't check that no other checker also evals. |
768 | #endif |
769 | } |
770 | } |
771 | |
772 | // If none of the checkers evaluated the call, ask ExprEngine to handle it. |
773 | if (!evaluatorChecker) { |
774 | NodeBuilder B(Pred, Dst, Eng.getBuilderContext()); |
775 | Eng.defaultEvalCall(B, Pred, Call, CallOpts); |
776 | } |
777 | } |
778 | } |
779 | |
780 | /// Run checkers for the entire Translation Unit. |
781 | void CheckerManager::runCheckersOnEndOfTranslationUnit( |
782 | const TranslationUnitDecl *TU, |
783 | AnalysisManager &mgr, |
784 | BugReporter &BR) { |
785 | for (const auto &EndOfTranslationUnitChecker : EndOfTranslationUnitCheckers) |
786 | EndOfTranslationUnitChecker(TU, mgr, BR); |
787 | } |
788 | |
789 | void CheckerManager::runCheckersForPrintStateJson(raw_ostream &Out, |
790 | ProgramStateRef State, |
791 | const char *NL, |
792 | unsigned int Space, |
793 | bool IsDot) const { |
794 | Indent(Out, Space, IsDot) << "\"checker_messages\": "; |
795 | |
796 | // Create a temporary stream to see whether we have any message. |
797 | SmallString<1024> TempBuf; |
798 | llvm::raw_svector_ostream TempOut(TempBuf); |
799 | unsigned int InnerSpace = Space + 2; |
800 | |
801 | // Create the new-line in JSON with enough space. |
802 | SmallString<128> NewLine; |
803 | llvm::raw_svector_ostream NLOut(NewLine); |
804 | NLOut << "\", "<< NL; // Inject the ending and a new line |
805 | Indent(Out&: NLOut, Space: InnerSpace, IsDot) << "\""; // then begin the next message. |
806 | |
807 | ++Space; |
808 | bool HasMessage = false; |
809 | |
810 | // Store the last CheckerTag. |
811 | const void *LastCT = nullptr; |
812 | for (const auto &CT : CheckerTags) { |
813 | // See whether the current checker has a message. |
814 | CT.second->printState(Out&: TempOut, State, /*NL=*/NewLine.c_str(), /*Sep=*/""); |
815 | |
816 | if (TempBuf.empty()) |
817 | continue; |
818 | |
819 | if (!HasMessage) { |
820 | Out << '[' << NL; |
821 | HasMessage = true; |
822 | } |
823 | |
824 | LastCT = &CT; |
825 | TempBuf.clear(); |
826 | } |
827 | |
828 | for (const auto &CT : CheckerTags) { |
829 | // See whether the current checker has a message. |
830 | CT.second->printState(Out&: TempOut, State, /*NL=*/NewLine.c_str(), /*Sep=*/""); |
831 | |
832 | if (TempBuf.empty()) |
833 | continue; |
834 | |
835 | Indent(Out, Space, IsDot) << "{ \"checker\": \""<< CT.second->getDebugTag() |
836 | << "\", \"messages\": ["<< NL; |
837 | Indent(Out, Space: InnerSpace, IsDot) |
838 | << '\"' << TempBuf.str().trim() << '\"' << NL; |
839 | Indent(Out, Space, IsDot) << "]}"; |
840 | |
841 | if (&CT != LastCT) |
842 | Out << ','; |
843 | Out << NL; |
844 | |
845 | TempBuf.clear(); |
846 | } |
847 | |
848 | // It is the last element of the 'program_state' so do not add a comma. |
849 | if (HasMessage) |
850 | Indent(Out, Space: --Space, IsDot) << "]"; |
851 | else |
852 | Out << "null"; |
853 | |
854 | Out << NL; |
855 | } |
856 | |
857 | //===----------------------------------------------------------------------===// |
858 | // Internal registration functions for AST traversing. |
859 | //===----------------------------------------------------------------------===// |
860 | |
861 | void CheckerManager::_registerForDecl(CheckDeclFunc checkfn, |
862 | HandlesDeclFunc isForDeclFn) { |
863 | DeclCheckerInfo info = { .CheckFn: checkfn, .IsForDeclFn: isForDeclFn }; |
864 | DeclCheckers.push_back(x: info); |
865 | } |
866 | |
867 | void CheckerManager::_registerForBody(CheckDeclFunc checkfn) { |
868 | BodyCheckers.push_back(x: checkfn); |
869 | } |
870 | |
871 | //===----------------------------------------------------------------------===// |
872 | // Internal registration functions for path-sensitive checking. |
873 | //===----------------------------------------------------------------------===// |
874 | |
875 | void CheckerManager::_registerForPreStmt(CheckStmtFunc checkfn, |
876 | HandlesStmtFunc isForStmtFn) { |
877 | StmtCheckerInfo info = { .CheckFn: checkfn, .IsForStmtFn: isForStmtFn, /*IsPreVisit*/true }; |
878 | StmtCheckers.push_back(x: info); |
879 | } |
880 | |
881 | void CheckerManager::_registerForPostStmt(CheckStmtFunc checkfn, |
882 | HandlesStmtFunc isForStmtFn) { |
883 | StmtCheckerInfo info = { .CheckFn: checkfn, .IsForStmtFn: isForStmtFn, /*IsPreVisit*/false }; |
884 | StmtCheckers.push_back(x: info); |
885 | } |
886 | |
887 | void CheckerManager::_registerForPreObjCMessage(CheckObjCMessageFunc checkfn) { |
888 | PreObjCMessageCheckers.push_back(x: checkfn); |
889 | } |
890 | |
891 | void CheckerManager::_registerForObjCMessageNil(CheckObjCMessageFunc checkfn) { |
892 | ObjCMessageNilCheckers.push_back(x: checkfn); |
893 | } |
894 | |
895 | void CheckerManager::_registerForPostObjCMessage(CheckObjCMessageFunc checkfn) { |
896 | PostObjCMessageCheckers.push_back(x: checkfn); |
897 | } |
898 | |
899 | void CheckerManager::_registerForPreCall(CheckCallFunc checkfn) { |
900 | PreCallCheckers.push_back(x: checkfn); |
901 | } |
902 | void CheckerManager::_registerForPostCall(CheckCallFunc checkfn) { |
903 | PostCallCheckers.push_back(x: checkfn); |
904 | } |
905 | |
906 | void CheckerManager::_registerForLocation(CheckLocationFunc checkfn) { |
907 | LocationCheckers.push_back(x: checkfn); |
908 | } |
909 | |
910 | void CheckerManager::_registerForBind(CheckBindFunc checkfn) { |
911 | BindCheckers.push_back(x: checkfn); |
912 | } |
913 | |
914 | void CheckerManager::_registerForBlockEntrance(CheckBlockEntranceFunc checkfn) { |
915 | BlockEntranceCheckers.push_back(x: checkfn); |
916 | } |
917 | |
918 | void CheckerManager::_registerForEndAnalysis(CheckEndAnalysisFunc checkfn) { |
919 | EndAnalysisCheckers.push_back(x: checkfn); |
920 | } |
921 | |
922 | void CheckerManager::_registerForBeginFunction(CheckBeginFunctionFunc checkfn) { |
923 | BeginFunctionCheckers.push_back(x: checkfn); |
924 | } |
925 | |
926 | void CheckerManager::_registerForEndFunction(CheckEndFunctionFunc checkfn) { |
927 | EndFunctionCheckers.push_back(x: checkfn); |
928 | } |
929 | |
930 | void CheckerManager::_registerForBranchCondition( |
931 | CheckBranchConditionFunc checkfn) { |
932 | BranchConditionCheckers.push_back(x: checkfn); |
933 | } |
934 | |
935 | void CheckerManager::_registerForNewAllocator(CheckNewAllocatorFunc checkfn) { |
936 | NewAllocatorCheckers.push_back(x: checkfn); |
937 | } |
938 | |
939 | void CheckerManager::_registerForLiveSymbols(CheckLiveSymbolsFunc checkfn) { |
940 | LiveSymbolsCheckers.push_back(x: checkfn); |
941 | } |
942 | |
943 | void CheckerManager::_registerForDeadSymbols(CheckDeadSymbolsFunc checkfn) { |
944 | DeadSymbolsCheckers.push_back(x: checkfn); |
945 | } |
946 | |
947 | void CheckerManager::_registerForRegionChanges(CheckRegionChangesFunc checkfn) { |
948 | RegionChangesCheckers.push_back(x: checkfn); |
949 | } |
950 | |
951 | void CheckerManager::_registerForPointerEscape(CheckPointerEscapeFunc checkfn){ |
952 | PointerEscapeCheckers.push_back(x: checkfn); |
953 | } |
954 | |
955 | void CheckerManager::_registerForConstPointerEscape( |
956 | CheckPointerEscapeFunc checkfn) { |
957 | PointerEscapeCheckers.push_back(x: checkfn); |
958 | } |
959 | |
960 | void CheckerManager::_registerForEvalAssume(EvalAssumeFunc checkfn) { |
961 | EvalAssumeCheckers.push_back(x: checkfn); |
962 | } |
963 | |
964 | void CheckerManager::_registerForEvalCall(EvalCallFunc checkfn) { |
965 | EvalCallCheckers.push_back(x: checkfn); |
966 | } |
967 | |
968 | void CheckerManager::_registerForEndOfTranslationUnit( |
969 | CheckEndOfTranslationUnit checkfn) { |
970 | EndOfTranslationUnitCheckers.push_back(x: checkfn); |
971 | } |
972 | |
973 | //===----------------------------------------------------------------------===// |
974 | // Implementation details. |
975 | //===----------------------------------------------------------------------===// |
976 | |
977 | const CheckerManager::CachedStmtCheckers & |
978 | CheckerManager::getCachedStmtCheckersFor(const Stmt *S, bool isPreVisit) { |
979 | assert(S); |
980 | |
981 | unsigned Key = (S->getStmtClass() << 1) | unsigned(isPreVisit); |
982 | auto [CCI, Inserted] = CachedStmtCheckersMap.try_emplace(Key); |
983 | CachedStmtCheckers &Checkers = CCI->second; |
984 | if (Inserted) { |
985 | // Find the checkers that should run for this Stmt and cache them. |
986 | for (const auto &Info : StmtCheckers) |
987 | if (Info.IsPreVisit == isPreVisit && Info.IsForStmtFn(S)) |
988 | Checkers.push_back(Elt: Info.CheckFn); |
989 | } |
990 | return Checkers; |
991 | } |
992 |
Definitions
- hasPathSensitiveCheckers
- reportInvalidCheckerOptionValue
- runCheckersOnASTDecl
- runCheckersOnASTBody
- expandGraphWithCheckers
- checkerScopeName
- CheckStmtContext
- CheckStmtContext
- checkers_begin
- checkers_end
- runChecker
- runCheckersForStmt
- CheckObjCMessageContext
- CheckObjCMessageContext
- checkers_begin
- checkers_end
- runChecker
- runCheckersForObjCMessage
- getObjCMessageCheckers
- CheckCallContext
- CheckCallContext
- checkers_begin
- checkers_end
- runChecker
- runCheckersForCallEvent
- CheckLocationContext
- CheckLocationContext
- checkers_begin
- checkers_end
- runChecker
- runCheckersForLocation
- CheckBindContext
- CheckBindContext
- checkers_begin
- checkers_end
- runChecker
- getTimeTraceBindMetadata
- runCheckersForBind
- CheckBlockEntranceContext
- CheckBlockEntranceContext
- checkers_begin
- checkers_end
- runChecker
- runCheckersForBlockEntrance
- runCheckersForEndAnalysis
- CheckBeginFunctionContext
- CheckBeginFunctionContext
- checkers_begin
- checkers_end
- runChecker
- runCheckersForBeginFunction
- runCheckersForEndFunction
- CheckBranchConditionContext
- CheckBranchConditionContext
- checkers_begin
- checkers_end
- runChecker
- runCheckersForBranchCondition
- CheckNewAllocatorContext
- CheckNewAllocatorContext
- checkers_begin
- checkers_end
- runChecker
- runCheckersForNewAllocator
- runCheckersForLiveSymbols
- CheckDeadSymbolsContext
- CheckDeadSymbolsContext
- checkers_begin
- checkers_end
- runChecker
- runCheckersForDeadSymbols
- runCheckersForRegionChanges
- runCheckersForPointerEscape
- runCheckersForEvalAssume
- runCheckersForEvalCall
- runCheckersOnEndOfTranslationUnit
- runCheckersForPrintStateJson
- _registerForDecl
- _registerForBody
- _registerForPreStmt
- _registerForPostStmt
- _registerForPreObjCMessage
- _registerForObjCMessageNil
- _registerForPostObjCMessage
- _registerForPreCall
- _registerForPostCall
- _registerForLocation
- _registerForBind
- _registerForBlockEntrance
- _registerForEndAnalysis
- _registerForBeginFunction
- _registerForEndFunction
- _registerForBranchCondition
- _registerForNewAllocator
- _registerForLiveSymbols
- _registerForDeadSymbols
- _registerForRegionChanges
- _registerForPointerEscape
- _registerForConstPointerEscape
- _registerForEvalAssume
- _registerForEvalCall
- _registerForEndOfTranslationUnit
Improve your Profiling and Debugging skills
Find out more