]> granicus.if.org Git - clang/commitdiff
[analyzer] Add an option to re-analyze a dead-end path without inlining.
authorAnna Zaks <ganna@apple.com>
Tue, 27 Mar 2012 20:02:53 +0000 (20:02 +0000)
committerAnna Zaks <ganna@apple.com>
Tue, 27 Mar 2012 20:02:53 +0000 (20:02 +0000)
The analyzer gives up path exploration under certain conditions. For
example, when the same basic block has been visited more than 4 times.
With inlining turned on, this could lead to decrease in code coverage.
Specifically, if we give up inside the inlined function, the rest of
parent's basic blocks will not get analyzed.

This commit introduces an option to enable re-run along the failed path,
in which we do not inline the last inlined call site. This is done by
enqueueing the node before the processing of the inlined call site
with a special policy encoded in the state. The policy tells us not to
inline the call site along the path.

This lead to ~10% increase in the number of paths analyzed. Even though
we expected a much greater coverage improvement.

The option is turned off by default for now.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@153534 91177308-0d34-0410-b5e6-96231b3b80d8

15 files changed:
include/clang/Analysis/ProgramPoint.h
include/clang/Driver/CC1Options.td
include/clang/Frontend/AnalyzerOptions.h
include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h
include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h
include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h
include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h
lib/Frontend/CompilerInvocation.cpp
lib/StaticAnalyzer/Core/AnalysisManager.cpp
lib/StaticAnalyzer/Core/CoreEngine.cpp
lib/StaticAnalyzer/Core/ExprEngine.cpp
lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
test/Analysis/coverage.c [new file with mode: 0644]

index 413b3cf4ea11b514f9a9f5fbe63d5602c2702cb6..e7bcf19bde3484580dc88a2cf5bb5136d4effcf9 100644 (file)
@@ -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.
index e8ec82271508062ec555643b7c05f51b83e127f5..46c4ed89b5c62c7acafc3266028039f2e798d076 100644 (file)
@@ -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)">;
index 4199df0b6eb752acf00195420ed05b9c0d836564..f152f639685501c9d3f767ac012b8ff481bf3afc 100644 (file)
@@ -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;
index 3e19b26328928116f111fff2fb12e4ac89e5c94b..b77e069041e4f52e1ddf2b03af6b535c40c3b9dc 100644 (file)
@@ -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.
index 18ad0078c6abb88199065f3a658ac1313800a52f..a9a4ae423095b453c1525a76c2a02f842376ac15 100644 (file)
@@ -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.
index 1c81af9586eb65937d10b170ebc42e9817a786e0..46fbb88212e3b955b3f331333be2819767a221c8 100644 (file)
@@ -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());
   }
index e8c4688293622a8161bd7157cbf24615c580a26b..2c144109b222790c41a2586e478b556806b2b832 100644 (file)
@@ -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
index e79b59358490ff1c1dd9e67956605224d3c8dc26..1c7bedb82f2486a3b0094959f316fd01a7cd1e39 100644 (file)
@@ -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
index 1dd501e982bf27639f099e977f74adee846b234f..0f68591c963cd635443e67e17564de89731a0800 100644 (file)
@@ -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 =
index 139bc88de9f25eb862891e91b78c3e068bc3b5db..057a5d490a64cb3084e50dfa15137b6cf45bc929 100644 (file)
@@ -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();
 }
index fac368c8f20e7748647370e1d815523248f6367c..f303c7af3a05586c3a0e8d753227095638a78ac2 100644 (file)
@@ -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());
index 1bbcf1e68927907b28a460eacd32f181ef3fe359..c47b7f4eb53e0ccd5d7153d52aa6d612fd47acff 100644 (file)
@@ -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();
index 66842050af9c4dc9ef71b17a7c8db1838e178cac..1746b5c529e4f966fc3d65f86e90d24b82211d99 100644 (file)
@@ -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.
index 2b8e77745e17ea2c84dc5a9680583fd69d1a9a15..3665955d61d5e7fe14904e056d4be803fd2eb9ad 100644 (file)
@@ -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 (file)
index 0000000..2e85b7f
--- /dev/null
@@ -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
+}