diff options
-rw-r--r-- | include/clang/Analysis/ProgramPoint.h | 18 | ||||
-rw-r--r-- | include/clang/Driver/CC1Options.td | 3 | ||||
-rw-r--r-- | include/clang/Frontend/AnalyzerOptions.h | 2 | ||||
-rw-r--r-- | include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h | 7 | ||||
-rw-r--r-- | include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h | 11 | ||||
-rw-r--r-- | include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h | 4 | ||||
-rw-r--r-- | include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h | 13 | ||||
-rw-r--r-- | include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h | 15 | ||||
-rw-r--r-- | lib/Frontend/CompilerInvocation.cpp | 3 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/AnalysisManager.cpp | 9 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/CoreEngine.cpp | 84 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngine.cpp | 84 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | 73 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp | 3 | ||||
-rw-r--r-- | test/Analysis/coverage.c | 94 |
15 files changed, 346 insertions, 77 deletions
diff --git a/include/clang/Analysis/ProgramPoint.h b/include/clang/Analysis/ProgramPoint.h index 413b3cf4ea..e7bcf19bde 100644 --- a/include/clang/Analysis/ProgramPoint.h +++ b/include/clang/Analysis/ProgramPoint.h @@ -51,7 +51,8 @@ public: CallEnterKind, CallExitKind, MinPostStmtKind = PostStmtKind, - MaxPostStmtKind = CallExitKind }; + MaxPostStmtKind = CallExitKind, + EpsilonKind}; private: std::pair<const void *, const void *> Data; @@ -380,6 +381,21 @@ public: } }; +/// This is a meta program point, which should be skipped by all the diagnostic +/// reasoning etc. +class EpsilonPoint : public ProgramPoint { +public: + EpsilonPoint(const LocationContext *L, const void *Data1, + const void *Data2 = 0, const ProgramPointTag *tag = 0) + : ProgramPoint(Data1, Data2, EpsilonKind, L, tag) {} + + const void *getData() const { return getData1(); } + + static bool classof(const ProgramPoint* Location) { + return Location->getKind() == EpsilonKind; + } +}; + /// ProgramPoints can be "tagged" as representing points specific to a given /// analysis entity. Tags are abstract annotations, with an associated /// description and potentially other information. diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index e8ec822715..46c4ed89b5 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -98,6 +98,9 @@ def analyzer_ipa_EQ : Joined<"-analyzer-ipa=">, Alias<analyzer_ipa>; def analyzer_inlining_mode : Separate<"-analyzer-inlining-mode">, HelpText<"Specify the function selection heuristic used during inlining">; def analyzer_inlining_mode_EQ : Joined<"-analyzer-inlining-mode=">, Alias<analyzer_inlining_mode>; + +def analyzer_retry_exhausted : Flag<"-analyzer-retry-exhausted">, + HelpText<"Re-analyze paths leading to exhausted nodes with a different strategy for better code coverage">; def analyzer_max_nodes : Separate<"-analyzer-max-nodes">, HelpText<"The maximum number of nodes the analyzer can generate (150000 default, 0 = no limit)">; diff --git a/include/clang/Frontend/AnalyzerOptions.h b/include/clang/Frontend/AnalyzerOptions.h index 4199df0b6e..f152f63968 100644 --- a/include/clang/Frontend/AnalyzerOptions.h +++ b/include/clang/Frontend/AnalyzerOptions.h @@ -99,6 +99,7 @@ public: unsigned CFGAddInitializers : 1; unsigned EagerlyTrimEGraph : 1; unsigned PrintStats : 1; + unsigned RetryExhausted : 1; unsigned InlineMaxStackDepth; unsigned InlineMaxFunctionSize; AnalysisInliningMode InliningMode; @@ -123,6 +124,7 @@ public: CFGAddInitializers = 0; EagerlyTrimEGraph = 0; PrintStats = 0; + RetryExhausted = 0; // Cap the stack depth at 4 calls (5 stack frames, base + 4 calls). InlineMaxStackDepth = 5; InlineMaxFunctionSize = 200; diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h index 3e19b26328..b77e069041 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h @@ -91,6 +91,10 @@ public: /// \brief The mode of function selection used during inlining. AnalysisInliningMode InliningMode; + /// \brief Re-analyze paths leading to exhausted nodes with a different + /// strategy for better code coverage. + bool RetryExhausted; + public: AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, const LangOptions &lang, PathDiagnosticConsumer *pd, @@ -107,7 +111,8 @@ public: AnalysisIPAMode ipa, unsigned inlineMaxStack, unsigned inlineMaxFunctionSize, - AnalysisInliningMode inliningMode); + AnalysisInliningMode inliningMode, + bool retry); /// Construct a clone of the given AnalysisManager with the given ASTContext /// and DiagnosticsEngine. diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h index 18ad0078c6..a9a4ae4230 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h @@ -98,9 +98,6 @@ private: CoreEngine(const CoreEngine&); // Do not implement. CoreEngine& operator=(const CoreEngine&); - void enqueueStmtNode(ExplodedNode *N, - const CFGBlock *Block, unsigned Idx); - ExplodedNode *generateCallExitNode(ExplodedNode *N); public: @@ -132,6 +129,11 @@ public: ProgramStateRef InitState, ExplodedNodeSet &Dst); + /// Dispatch the work list item based on the given location information. + /// Use Pred parameter as the predecessor state. + void dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, + const WorkListUnit& WU); + // Functions for external checking of whether we have unfinished work bool wasBlockAborted() const { return !blocksAborted.empty(); } bool wasBlocksExhausted() const { return !blocksExhausted.empty(); } @@ -170,6 +172,9 @@ public: /// \brief enqueue the nodes corresponding to the end of function onto the /// end of path / work list. void enqueueEndOfFunction(ExplodedNodeSet &Set); + + /// \brief Enqueue a single node created as a result of statement processing. + void enqueueStmtNode(ExplodedNode *N, const CFGBlock *Block, unsigned Idx); }; // TODO: Turn into a calss. diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h index 1c81af9586..46fbb88212 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h @@ -173,6 +173,10 @@ public: bool isSink() const { return Succs.getFlag(); } + bool hasSinglePred() const { + return (pred_size() == 1); + } + ExplodedNode *getFirstPred() { return pred_empty() ? NULL : *(pred_begin()); } diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index e8c4688293..2c144109b2 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -20,6 +20,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/AST/Expr.h" #include "clang/AST/Type.h" @@ -455,7 +456,19 @@ private: ProgramStateRef St, SVal location, const ProgramPointTag *tag, bool isLoad); + bool shouldInlineDecl(const FunctionDecl *FD, ExplodedNode *Pred); bool InlineCall(ExplodedNodeSet &Dst, const CallExpr *CE, ExplodedNode *Pred); + + bool replayWithoutInlining(ExplodedNode *P, const LocationContext *CalleeLC); +}; + +/// Traits for storing the call processing policy inside GDM. +/// The GDM stores the corresponding CallExpr pointer. +struct ReplayWithoutInlining{}; +template <> +struct ProgramStateTrait<ReplayWithoutInlining> : + public ProgramStatePartialTrait<void*> { + static void *GDMIndex() { static int index = 0; return &index; } }; } // end ento namespace diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h index e79b593584..1c7bedb82f 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h @@ -176,7 +176,20 @@ namespace ento { return (void*) (uintptr_t) d; } }; - + + // Partial specialization for void*. + template <> struct ProgramStatePartialTrait<void*> { + typedef void *data_type; + + static inline data_type MakeData(void *const* p) { + return p ? *p + : data_type(); + } + static inline void *MakeVoidPtr(data_type d) { + return d; + } + }; + } // end GR namespace } // end clang namespace diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 1dd501e982..0f68591c96 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -167,6 +167,8 @@ static void AnalyzerOptsToArgs(const AnalyzerOptions &Opts, ToArgsList &Res) { Res.push_back("-analyzer-viz-egraph-graphviz"); if (Opts.VisualizeEGUbi) Res.push_back("-analyzer-viz-egraph-ubigraph"); + if (Opts.RetryExhausted) + Res.push_back("-analyzer-retry-exhausted"); for (unsigned i = 0, e = Opts.CheckersControlList.size(); i != e; ++i) { const std::pair<std::string, bool> &opt = Opts.CheckersControlList[i]; @@ -1012,6 +1014,7 @@ static bool ParseAnalyzerArgs(AnalyzerOptions &Opts, ArgList &Args, Opts.ShowCheckerHelp = Args.hasArg(OPT_analyzer_checker_help); Opts.VisualizeEGDot = Args.hasArg(OPT_analyzer_viz_egraph_graphviz); Opts.VisualizeEGUbi = Args.hasArg(OPT_analyzer_viz_egraph_ubigraph); + Opts.RetryExhausted = Args.hasArg(OPT_analyzer_retry_exhausted); Opts.AnalyzeAll = Args.hasArg(OPT_analyzer_opt_analyze_headers); Opts.AnalyzerDisplayProgress = Args.hasArg(OPT_analyzer_display_progress); Opts.AnalyzeNestedBlocks = diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp index 139bc88de9..057a5d490a 100644 --- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -33,7 +33,8 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, AnalysisIPAMode ipa, unsigned inlineMaxStack, unsigned inlineMaxFunctionSize, - AnalysisInliningMode IMode) + AnalysisInliningMode IMode, + bool retry) : AnaCtxMgr(useUnoptimizedCFG, addImplicitDtors, addInitializers), Ctx(ctx), Diags(diags), LangOpts(lang), PD(pd), CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr), @@ -45,7 +46,8 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, IPAMode(ipa), InlineMaxStackDepth(inlineMaxStack), InlineMaxFunctionSize(inlineMaxFunctionSize), - InliningMode(IMode) + InliningMode(IMode), + RetryExhausted(retry) { AnaCtxMgr.getCFGBuildOptions().setAllAlwaysAdd(); } @@ -73,7 +75,8 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, IPAMode(ParentAM.IPAMode), InlineMaxStackDepth(ParentAM.InlineMaxStackDepth), InlineMaxFunctionSize(ParentAM.InlineMaxFunctionSize), - InliningMode(ParentAM.InliningMode) + InliningMode(ParentAM.InliningMode), + RetryExhausted(ParentAM.RetryExhausted) { AnaCtxMgr.getCFGBuildOptions().setAllAlwaysAdd(); } diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp index fac368c8f2..f303c7af3a 100644 --- a/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -211,45 +211,56 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, // Retrieve the node. ExplodedNode *Node = WU.getNode(); - // Dispatch on the location type. - switch (Node->getLocation().getKind()) { - case ProgramPoint::BlockEdgeKind: - HandleBlockEdge(cast<BlockEdge>(Node->getLocation()), Node); - break; - - case ProgramPoint::BlockEntranceKind: - HandleBlockEntrance(cast<BlockEntrance>(Node->getLocation()), Node); - break; - - case ProgramPoint::BlockExitKind: - assert (false && "BlockExit location never occur in forward analysis."); - break; + dispatchWorkItem(Node, Node->getLocation(), WU); + } + SubEng.processEndWorklist(hasWorkRemaining()); + return WList->hasWork(); +} - case ProgramPoint::CallEnterKind: { - CallEnter CEnter = cast<CallEnter>(Node->getLocation()); - if (AnalyzedCallees) - if (const CallExpr* CE = - dyn_cast_or_null<CallExpr>(CEnter.getCallExpr())) - if (const Decl *CD = CE->getCalleeDecl()) - AnalyzedCallees->insert(CD); - SubEng.processCallEnter(CEnter, Node); - break; - } +void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, + const WorkListUnit& WU) { + // Dispatch on the location type. + switch (Loc.getKind()) { + case ProgramPoint::BlockEdgeKind: + HandleBlockEdge(cast<BlockEdge>(Loc), Pred); + break; + + case ProgramPoint::BlockEntranceKind: + HandleBlockEntrance(cast<BlockEntrance>(Loc), Pred); + break; + + case ProgramPoint::BlockExitKind: + assert (false && "BlockExit location never occur in forward analysis."); + break; + + case ProgramPoint::CallEnterKind: { + CallEnter CEnter = cast<CallEnter>(Loc); + if (AnalyzedCallees) + if (const CallExpr* CE = + dyn_cast_or_null<CallExpr>(CEnter.getCallExpr())) + if (const Decl *CD = CE->getCalleeDecl()) + AnalyzedCallees->insert(CD); + SubEng.processCallEnter(CEnter, Pred); + break; + } - case ProgramPoint::CallExitKind: - SubEng.processCallExit(Node); - break; + case ProgramPoint::CallExitKind: + SubEng.processCallExit(Pred); + break; - default: - assert(isa<PostStmt>(Node->getLocation()) || - isa<PostInitializer>(Node->getLocation())); - HandlePostStmt(WU.getBlock(), WU.getIndex(), Node); - break; + case ProgramPoint::EpsilonKind: { + assert(Pred->hasSinglePred() && + "Assume epsilon has exactly one predecessor by construction"); + ExplodedNode *PNode = Pred->getFirstPred(); + dispatchWorkItem(Pred, PNode->getLocation(), WU); + break; } + default: + assert(isa<PostStmt>(Loc) || + isa<PostInitializer>(Loc)); + HandlePostStmt(WU.getBlock(), WU.getIndex(), Pred); + break; } - - SubEng.processEndWorklist(hasWorkRemaining()); - return WList->hasWork(); } void CoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L, @@ -491,6 +502,11 @@ void CoreEngine::enqueueStmtNode(ExplodedNode *N, return; } + if (isa<EpsilonPoint>(N->getLocation())) { + WList->enqueue(N, Block, Idx); + return; + } + const CFGStmt *CS = (*Block)[Idx].getAs<CFGStmt>(); const Stmt *St = CS ? CS->getStmt() : 0; PostStmt Loc(St, N->getLocationContext()); diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 1bbcf1e689..c47b7f4eb5 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -50,6 +50,13 @@ STATISTIC(NumMaxBlockCountReached, STATISTIC(NumMaxBlockCountReachedInInlined, "The # of aborted paths due to reaching the maximum block count in " "an inlined function"); +STATISTIC(NumTimesRetriedWithoutInlining, + "The # of times we re-evaluated a call without inlining"); + +STATISTIC(NumNotNew, + "Cached out"); +STATISTIC(NumNull, + "Null node"); //===----------------------------------------------------------------------===// // Utility functions. @@ -972,6 +979,64 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, } } +bool ExprEngine::replayWithoutInlining(ExplodedNode *N, + const LocationContext *CalleeLC) { + const StackFrameContext *CalleeSF = CalleeLC->getCurrentStackFrame(); + const StackFrameContext *CallerSF = CalleeSF->getParent()->getCurrentStackFrame(); + assert(CalleeSF && CallerSF); + ExplodedNode *BeforeProcessingCall = 0; + + // Find the first node before we started processing the call expression. + while (N) { + ProgramPoint L = N->getLocation(); + BeforeProcessingCall = N; + N = N->pred_empty() ? NULL : *(N->pred_begin()); + + // Skip the nodes corresponding to the inlined code. + if (L.getLocationContext()->getCurrentStackFrame() != CallerSF) + continue; + // We reached the caller. Find the node right before we started + // processing the CallExpr. + if (isa<PostPurgeDeadSymbols>(L)) + continue; + if (const StmtPoint *SP = dyn_cast<StmtPoint>(&L)) + if (SP->getStmt() == CalleeSF->getCallSite()) + continue; + break; + } + + if (!BeforeProcessingCall) { + NumNull++; + return false; + } + + // TODO: Clean up the unneeded nodes. + + // Build an Epsilon node from which we will restart the analyzes. + const Stmt *CE = CalleeSF->getCallSite(); + ProgramPoint NewNodeLoc = + EpsilonPoint(BeforeProcessingCall->getLocationContext(), CE); + // Add the special flag to GDM to signal retrying with no inlining. + // Note, changing the state ensures that we are not going to cache out. + ProgramStateRef NewNodeState = BeforeProcessingCall->getState(); + NewNodeState = NewNodeState->set<ReplayWithoutInlining>((void*)CE); + + // Make the new node a successor of BeforeProcessingCall. + bool IsNew = false; + ExplodedNode *NewNode = G.getNode(NewNodeLoc, NewNodeState, false, &IsNew); + if (!IsNew) { + NumNotNew++; + return false; + } + NewNode->addPredecessor(BeforeProcessingCall, G); + + // Add the new node to the work list. + Engine.enqueueStmtNode(NewNode, CalleeSF->getCallSiteBlock(), + CalleeSF->getIndex()); + NumTimesRetriedWithoutInlining++; + return true; +} + /// Block entrance. (Update counters). void ExprEngine::processCFGBlockEntrance(NodeBuilderWithSinks &nodeBuilder) { @@ -984,10 +1049,17 @@ void ExprEngine::processCFGBlockEntrance(NodeBuilderWithSinks &nodeBuilder) { // Check if we stopped at the top level function or not. // Root node should have the location context of the top most function. - if ((*G.roots_begin())->getLocation().getLocationContext() != - pred->getLocation().getLocationContext()) - NumMaxBlockCountReachedInInlined++; - else + const LocationContext *CalleeLC = pred->getLocation().getLocationContext(); + const LocationContext *RootLC = + (*G.roots_begin())->getLocation().getLocationContext(); + if (RootLC->getCurrentStackFrame() != CalleeLC->getCurrentStackFrame()) { + // Re-run the call evaluation without inlining it, by storing the + // no-inlining policy in the state and enqueuing the new work item on + // the list. Replay should almost never fail. Use the stats to catch it + // if it does. + if (!(AMgr.RetryExhausted && replayWithoutInlining(pred, CalleeLC))) + NumMaxBlockCountReachedInInlined++; + } else NumMaxBlockCountReached++; } } @@ -1788,6 +1860,10 @@ struct DOTGraphTraits<ExplodedNode*> : Out << "CallExit"; break; + case ProgramPoint::EpsilonKind: + Out << "Epsilon Point"; + break; + default: { if (StmtPoint *L = dyn_cast<StmtPoint>(&Loc)) { const Stmt *S = L->getStmt(); diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 66842050af..1746b5c529 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -129,8 +129,7 @@ static unsigned getNumberStackFrames(const LocationContext *LCtx) { } // Determine if we should inline the call. -static bool shouldInline(const FunctionDecl *FD, ExplodedNode *Pred, - AnalysisManager &AMgr) { +bool ExprEngine::shouldInlineDecl(const FunctionDecl *FD, ExplodedNode *Pred) { AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(FD); const CFG *CalleeCFG = CalleeADC->getCFG(); @@ -144,9 +143,35 @@ static bool shouldInline(const FunctionDecl *FD, ExplodedNode *Pred, return true; } +// For now, skip inlining variadic functions. +// We also don't inline blocks. +static bool shouldInlineCallExpr(const CallExpr *CE, ExprEngine *E) { + if (!E->getAnalysisManager().shouldInlineCall()) + return false; + QualType callee = CE->getCallee()->getType(); + const FunctionProtoType *FT = 0; + if (const PointerType *PT = callee->getAs<PointerType>()) + FT = dyn_cast<FunctionProtoType>(PT->getPointeeType()); + else if (const BlockPointerType *BT = callee->getAs<BlockPointerType>()) { + // FIXME: inline blocks. + // FT = dyn_cast<FunctionProtoType>(BT->getPointeeType()); + (void) BT; + return false; + } + // If we have no prototype, assume the function is okay. + if (!FT) + return true; + + // Skip inlining of variadic functions. + return !FT->isVariadic(); +} + bool ExprEngine::InlineCall(ExplodedNodeSet &Dst, const CallExpr *CE, ExplodedNode *Pred) { + if (!shouldInlineCallExpr(CE, this)) + return false; + ProgramStateRef state = Pred->getState(); const Expr *Callee = CE->getCallee(); const FunctionDecl *FD = @@ -159,7 +184,7 @@ bool ExprEngine::InlineCall(ExplodedNodeSet &Dst, // FIXME: Handle C++. break; case Stmt::CallExprClass: { - if (!shouldInline(FD, Pred, AMgr)) + if (!shouldInlineDecl(FD, Pred)) return false; // Construct a new stack frame for the callee. @@ -346,28 +371,16 @@ ExprEngine::invalidateArguments(ProgramStateRef State, } -// For now, skip inlining variadic functions. -// We also don't inline blocks. -static bool shouldInlineCall(const CallExpr *CE, ExprEngine &Eng) { - if (!Eng.getAnalysisManager().shouldInlineCall()) - return false; - QualType callee = CE->getCallee()->getType(); - const FunctionProtoType *FT = 0; - if (const PointerType *PT = callee->getAs<PointerType>()) - FT = dyn_cast<FunctionProtoType>(PT->getPointeeType()); - else if (const BlockPointerType *BT = callee->getAs<BlockPointerType>()) { - // FIXME: inline blocks. - // FT = dyn_cast<FunctionProtoType>(BT->getPointeeType()); - (void) BT; - return false; +static ProgramStateRef getReplayWithoutInliningState(ExplodedNode *&N, + const CallExpr *CE) { + void *ReplayState = N->getState()->get<ReplayWithoutInlining>(); + if (!ReplayState) + return 0; + const CallExpr *ReplayCE = reinterpret_cast<const CallExpr*>(ReplayState); + if (CE == ReplayCE) { + return N->getState()->remove<ReplayWithoutInlining>(); } - - // If we have no prototype, assume the function is okay. - if (!FT) - return true; - - // Skip inlining of variadic functions. - return !FT->isVariadic(); + return 0; } void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, @@ -385,18 +398,20 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, DefaultEval(ExprEngine &eng, const CallExpr *ce) : Eng(eng), CE(ce) {} virtual void expandGraph(ExplodedNodeSet &Dst, ExplodedNode *Pred) { - // Should we inline the call? - if (shouldInlineCall(CE, Eng) && - Eng.InlineCall(Dst, CE, Pred)) { + + ProgramStateRef state = getReplayWithoutInliningState(Pred, CE); + + // First, try to inline the call. + if (state == 0 && Eng.InlineCall(Dst, CE, Pred)) return; - } // First handle the return value. StmtNodeBuilder Bldr(Pred, Dst, *Eng.currentBuilderContext); // Get the callee. const Expr *Callee = CE->getCallee()->IgnoreParens(); - ProgramStateRef state = Pred->getState(); + if (state == 0) + state = Pred->getState(); SVal L = state->getSVal(Callee, Pred->getLocationContext()); // Figure out the result type. We do this dance to handle references. diff --git a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index 2b8e77745e..3665955d61 100644 --- a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -203,7 +203,8 @@ public: Opts.IPAMode, Opts.InlineMaxStackDepth, Opts.InlineMaxFunctionSize, - Opts.InliningMode)); + Opts.InliningMode, + Opts.RetryExhausted)); } virtual void HandleTranslationUnit(ASTContext &C); diff --git a/test/Analysis/coverage.c b/test/Analysis/coverage.c new file mode 100644 index 0000000000..2e85b7f61d --- /dev/null +++ b/test/Analysis/coverage.c @@ -0,0 +1,94 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc -analyzer-store=region -analyzer-max-loop 4 -analyzer-retry-exhausted -verify %s +#include "system-header-simulator.h" + +typedef __typeof(sizeof(int)) size_t; +void *malloc(size_t); + +static int another_function(int *y) { + if (*y > 0) + return *y; + return 0; +} + +static void function_which_doesnt_give_up(int **x) { + *x = 0; +} + +static void function_which_gives_up(int *x) { + for (int i = 0; i < 5; ++i) + (*x)++; +} + +static void function_which_gives_up_nested(int *x) { + function_which_gives_up(x); + for (int i = 0; i < 5; ++i) + (*x)++; +} + +static void function_which_doesnt_give_up_nested(int *x, int *y) { + *y = another_function(x); + function_which_gives_up(x); +} + +void coverage1(int *x) { + function_which_gives_up(x); + char *m = (char*)malloc(12); // expected-warning {{potential leak}} +} + +void coverage2(int *x) { + if (x) { + function_which_gives_up(x); + char *m = (char*)malloc(12);// expected-warning {{potential leak}} + } +} + +void coverage3(int *x) { + x++; + function_which_gives_up(x); + char *m = (char*)malloc(12);// expected-warning {{potential leak}} +} + +void coverage4(int *x) { + *x += another_function(x); + function_which_gives_up(x); + char *m = (char*)malloc(12);// expected-warning {{potential leak}} +} + +void coverage5(int *x) { + for (int i = 0; i<7; ++i) + function_which_gives_up(x); + // The root function gives up here. + char *m = (char*)malloc(12); // no-warning +} + +void coverage6(int *x) { + for (int i = 0; i<3; ++i) { + function_which_gives_up(x); + } + char *m = (char*)malloc(12); // expected-warning {{potential leak}} +} + +int coverage7_inline(int *i) { + function_which_doesnt_give_up(&i); + return *i; // expected-warning {{Dereference}} +} + +void coverage8(int *x) { + int y; + function_which_doesnt_give_up_nested(x, &y); + y = (*x)/y; // expected-warning {{Division by zero}} + char *m = (char*)malloc(12); // expected-warning {{potential leak}} +} + +void function_which_gives_up_settonull(int **x) { + *x = 0; + int y = 0; + for (int i = 0; i < 5; ++i) + y++; +} + +void coverage9(int *x) { + int y = 5; + function_which_gives_up_settonull(&x); + y = (*x); // no warning +} |