]> granicus.if.org Git - clang/commitdiff
Added --trim-path-graph to the driver to trim paths from the ExplodedGraph
authorTed Kremenek <kremenek@apple.com>
Fri, 7 Mar 2008 22:58:01 +0000 (22:58 +0000)
committerTed Kremenek <kremenek@apple.com>
Fri, 7 Mar 2008 22:58:01 +0000 (22:58 +0000)
that are not related to error nodes.

Fixed bug where we did not detect some NULL dereferences.

Added "ExplodedGraph::Trim" to trim all nodes that cannot transitively reach
a set of provided nodes.

Fixed subtle bug in ExplodedNodeImpl where we could create predecessor
iterators that included the mangled "sink" bit.  The better fix is to integrate
this bit into the void* for the wrapped State, not the NodeGroups representing
a node's predecessors and successors.

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

Analysis/ExplodedGraph.cpp
Analysis/GRExprEngine.cpp
Analysis/GRSimpleVals.cpp
Driver/ASTConsumers.cpp
Driver/ASTConsumers.h
Driver/clang.cpp
include/clang/Analysis/Analyses/GRSimpleVals.h
include/clang/Analysis/PathSensitive/ExplodedGraph.h
include/clang/Analysis/PathSensitive/GRExprEngine.h

index 274565bf6c886d34b382542a7d6fcfe00dee0b35..421b9e3846b6723cf15df48ebf5fe61563469d1b 100644 (file)
@@ -13,6 +13,9 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/Analysis/PathSensitive/ExplodedGraph.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallVector.h"
 #include <vector>
 
 using namespace clang;
@@ -25,6 +28,7 @@ static inline std::vector<ExplodedNodeImpl*>& getVector(void* P) {
 void ExplodedNodeImpl::NodeGroup::addNode(ExplodedNodeImpl* N) {
   
   assert ((reinterpret_cast<uintptr_t>(N) & Mask) == 0x0);
+  assert (!getFlag());
   
   if (getKind() == Size1) {
     if (ExplodedNodeImpl* NOld = getNode()) {
@@ -47,14 +51,11 @@ void ExplodedNodeImpl::NodeGroup::addNode(ExplodedNodeImpl* N) {
   }
 }
 
-bool ExplodedNodeImpl::NodeGroup::empty() const {
-  if (getKind() == Size1)
-    return getNode() ? false : true;
-  else
-    return getVector(getPtr()).empty();
-}
 
 unsigned ExplodedNodeImpl::NodeGroup::size() const {
+  if (getFlag())
+    return 0;
+  
   if (getKind() == Size1)
     return getNode() ? 1 : 0;
   else
@@ -62,15 +63,21 @@ unsigned ExplodedNodeImpl::NodeGroup::size() const {
 }
 
 ExplodedNodeImpl** ExplodedNodeImpl::NodeGroup::begin() const {
+  if (getFlag())
+    return NULL;
+  
   if (getKind() == Size1)
-    return (ExplodedNodeImpl**) &P;
+    return (ExplodedNodeImpl**) (getPtr() ? &P : NULL);
   else
     return const_cast<ExplodedNodeImpl**>(&*(getVector(getPtr()).begin()));
 }
 
 ExplodedNodeImpl** ExplodedNodeImpl::NodeGroup::end() const {
+  if (getFlag())
+    return NULL;
+  
   if (getKind() == Size1)
-    return (ExplodedNodeImpl**) (P ? &P+1 : &P);
+    return (ExplodedNodeImpl**) (getPtr() ? &P+1 : NULL);
   else
     return const_cast<ExplodedNodeImpl**>(&*(getVector(getPtr()).end()));
 }
@@ -78,3 +85,110 @@ ExplodedNodeImpl** ExplodedNodeImpl::NodeGroup::end() const {
 ExplodedNodeImpl::NodeGroup::~NodeGroup() {
   if (getKind() == SizeOther) delete &getVector(getPtr());
 }
+
+ExplodedGraphImpl* ExplodedGraphImpl::Trim(ExplodedNodeImpl** BeginSources,
+                                           ExplodedNodeImpl** EndSources) const{
+  
+  typedef llvm::DenseSet<ExplodedNodeImpl*> Pass1Ty;
+  typedef llvm::DenseMap<ExplodedNodeImpl*, ExplodedNodeImpl*> Pass2Ty;
+  
+  Pass1Ty Pass1;
+  Pass2Ty Pass2;
+  
+  llvm::SmallVector<ExplodedNodeImpl*, 10> WL2;
+
+  { // ===- Pass 1 (reverse BFS) -===
+    
+    // Enqueue the source nodes to the first worklist. 
+    
+    llvm::SmallVector<ExplodedNodeImpl*, 10> WL1;
+  
+    for (ExplodedNodeImpl** I = BeginSources; I != EndSources; ++I)
+      WL1.push_back(*I);
+    
+    // Process the worklist.
+
+    while (!WL1.empty()) {
+      
+      ExplodedNodeImpl* N = WL1.back();
+      WL1.pop_back();
+      
+      if (Pass1.count(N))
+        continue;
+      
+      Pass1.insert(N);
+      
+      if (N->Preds.empty()) {
+        WL2.push_back(N);
+        continue;      
+      }
+      
+      for (ExplodedNodeImpl** I=N->Preds.begin(), **E=N->Preds.end(); I!=E; ++I)
+        WL1.push_back(*I);
+    }
+  }
+  
+  if (WL2.empty())
+    return NULL;
+    
+  ExplodedGraphImpl* G = MakeEmptyGraph();
+  
+  // ===- Pass 2 (forward DFS to construct the new graph) -===
+  
+  while (!WL2.empty()) {
+    
+    ExplodedNodeImpl* N = WL2.back();
+    WL2.pop_back();
+    
+    // Skip this node if we have already processed it.
+    
+    if (Pass2.find(N) != Pass2.end())
+      continue;
+    
+    // Create the corresponding node in the new graph.
+    
+    ExplodedNodeImpl* NewN = G->getNodeImpl(N->getLocation(), N->State, NULL);
+    Pass2[N] = NewN;
+    
+    if (N->Preds.empty())
+      G->addRoot(NewN);
+    
+    // In the case that some of the intended predecessors of NewN have already
+    // been created, we should hook them up as predecessors.
+    
+    for (ExplodedNodeImpl **I=N->Preds.begin(), **E=N->Preds.end(); I!=E; ++I) {
+        
+      Pass2Ty::iterator PI = Pass2.find(*I);
+
+      if (PI == Pass2.end())
+        continue;
+      
+      NewN->addPredecessor(PI->second);
+    }
+
+    // In the case that some of the intended successors of NewN have already
+    // been created, we should hook them up as successors.  Otherwise, enqueue
+    // the new nodes from the original graph that should have nodes created
+    // in the new graph.
+   
+    for (ExplodedNodeImpl **I=N->Succs.begin(), **E=N->Succs.end(); I!=E; ++I) {
+      
+      Pass2Ty::iterator PI = Pass2.find(*I);
+      
+      if (PI != Pass2.end()) {
+        PI->second->addPredecessor(NewN);
+        continue;
+      }
+
+      // Enqueue nodes to the worklist that were marked during pass 1.
+      
+      if (Pass1.count(*I))
+        WL2.push_back(*I);
+    }
+    
+    if (N->isSink())
+      NewN->markAsSink();
+  }
+    
+  return G;
+}
index aaef2f8cb08260f208b2d683dd1f91fc90729fb3..3bf0151180804481fff25e54e8770ec660596fd0 100644 (file)
@@ -732,7 +732,8 @@ void GRExprEngine::VisitSizeOfAlignOfTypeExpr(SizeOfAlignOfTypeExpr* Ex,
   
 }
 
-void GRExprEngine::VisitDeref(UnaryOperator* U, NodeTy* Pred, NodeSet& Dst) {
+void GRExprEngine::VisitDeref(UnaryOperator* U, NodeTy* Pred,
+                              NodeSet& Dst, bool GetLVal) {
 
   Expr* Ex = U->getSubExpr()->IgnoreParens();
     
@@ -787,12 +788,16 @@ void GRExprEngine::VisitDeref(UnaryOperator* U, NodeTy* Pred, NodeSet& Dst) {
     ValueState* StNotNull = Assume(St, LV, true, isFeasibleNotNull);
     
     if (isFeasibleNotNull) {
+
+      if (GetLVal) Nodify(Dst, U, N, SetRVal(StNotNull, U, LV));
+      else {
+        
+        // FIXME: Currently symbolic analysis "generates" new symbols
+        //  for the contents of values.  We need a better approach.
       
-      // FIXME: Currently symbolic analysis "generates" new symbols
-      //  for the contents of values.  We need a better approach.
-      
-      Nodify(Dst, U, N, SetRVal(StNotNull, U,
-                                GetRVal(StNotNull, LV, U->getType())));
+        Nodify(Dst, U, N, SetRVal(StNotNull, U,
+                                  GetRVal(StNotNull, LV, U->getType())));
+      }
     }
     
     bool isFeasibleNull;
@@ -975,18 +980,11 @@ void GRExprEngine::VisitLVal(Expr* Ex, NodeTy* Pred, NodeSet& Dst) {
     return;
   }
   
-  if (UnaryOperator* U = dyn_cast<UnaryOperator>(Ex)) {
+  if (UnaryOperator* U = dyn_cast<UnaryOperator>(Ex))
     if (U->getOpcode() == UnaryOperator::Deref) {
-      Ex = U->getSubExpr()->IgnoreParens();
-      
-      if (isa<DeclRefExpr>(Ex))
-        Dst.Add(Pred);
-      else
-        Visit(Ex, Pred, Dst);
-      
+      VisitDeref(U, Pred, Dst, true);
       return;
     }
-  }
   
   Visit(Ex, Pred, Dst);
 }
@@ -1810,11 +1808,40 @@ struct VISIBILITY_HIDDEN DOTGraphTraits<GRExprEngine::NodeTy*> :
 } // end llvm namespace    
 #endif
 
-void GRExprEngine::ViewGraph() {
+#ifndef NDEBUG
+template <typename ITERATOR>
+static void AddSources(llvm::SmallVector<GRExprEngine::NodeTy*, 10>& Sources,
+                              ITERATOR I, ITERATOR E) {
+  
+  for ( ; I != E; ++I )
+    Sources.push_back(*I);
+}
+#endif
+
+void GRExprEngine::ViewGraph(bool trim) {
 #ifndef NDEBUG
   GraphPrintCheckerState = this;
   GraphPrintSourceManager = &getContext().getSourceManager();
-  llvm::ViewGraph(*G.roots_begin(), "GRExprEngine");
+  
+  if (trim) {
+    llvm::SmallVector<NodeTy*, 10> Sources;
+    AddSources(Sources, null_derefs_begin(), null_derefs_end());
+    AddSources(Sources, undef_derefs_begin(), undef_derefs_end());
+    
+    GRExprEngine::GraphTy* TrimmedG = G.Trim(&Sources[0],
+                                             &Sources[0]+Sources.size());
+    
+    if (!TrimmedG)
+      llvm::cerr << "warning: Trimmed ExplodedGraph is empty.\n";
+    else {
+      llvm::ViewGraph(*TrimmedG->roots_begin(), "TrimmedGRExprEngine");    
+      delete TrimmedG;
+    }
+  }
+  else
+    llvm::ViewGraph(*G.roots_begin(), "GRExprEngine");
+  
+  
   GraphPrintCheckerState = NULL;
   GraphPrintSourceManager = NULL;
 #endif
index c18610d9244ea07b80c6142f418ed51964f96e0c..efe435b884aa4b0df56dab45ab21d6701a6bb6ff 100644 (file)
@@ -90,7 +90,7 @@ static void EmitWarning(Diagnostic& Diag, SourceManager& SrcMgr,
 }
   
 unsigned RunGRSimpleVals(CFG& cfg, FunctionDecl& FD, ASTContext& Ctx,
-                         Diagnostic& Diag, bool Visualize) {
+                         Diagnostic& Diag, bool Visualize, bool TrimGraph) {
   
   if (Diag.hasErrorOccurred())
     return 0;
@@ -141,7 +141,7 @@ unsigned RunGRSimpleVals(CFG& cfg, FunctionDecl& FD, ASTContext& Ctx,
       "Pass-by-value argument in function or message expression is undefined.");
       
 #ifndef NDEBUG
-  if (Visualize) CheckerState->ViewGraph();
+  if (Visualize) CheckerState->ViewGraph(TrimGraph);
 #endif
   
   return Engine.getGraph().size();
index f8fbdc917300f49916a430aadec66485fbb67a5e..a24cd0478e014bb9c32d02e1dc08726efb632e72 100644 (file)
@@ -593,10 +593,11 @@ namespace {
     Diagnostic &Diags;
     ASTContext* Ctx;
     bool Visualize;
+    bool TrimGraph;
   public:
     GRSimpleValsVisitor(Diagnostic &diags, const std::string& fname,
-                        bool visualize)
-      : CFGVisitor(fname), Diags(diags), Visualize(visualize) {}
+                        bool visualize, bool trim)
+      : CFGVisitor(fname), Diags(diags), Visualize(visualize), TrimGraph(trim){}
     
     virtual void Initialize(ASTContext &Context) { Ctx = &Context; }    
     virtual void VisitCFG(CFG& C, FunctionDecl&);
@@ -606,9 +607,9 @@ namespace {
 
 ASTConsumer* clang::CreateGRSimpleVals(Diagnostic &Diags,
                                        const std::string& FunctionName,
-                                       bool Visualize) {
+                                       bool Visualize, bool TrimGraph) {
   
-  return new GRSimpleValsVisitor(Diags, FunctionName, Visualize);
+  return new GRSimpleValsVisitor(Diags, FunctionName, Visualize, TrimGraph);
 }
 
 void GRSimpleValsVisitor::VisitCFG(CFG& C, FunctionDecl& FD) {
@@ -626,13 +627,13 @@ void GRSimpleValsVisitor::VisitCFG(CFG& C, FunctionDecl& FD) {
 
     llvm::Timer T("GRSimpleVals");
     T.startTimer();
-    unsigned size = RunGRSimpleVals(C, FD, *Ctx, Diags, Visualize);
+    unsigned size = RunGRSimpleVals(C, FD, *Ctx, Diags, false, false);
     T.stopTimer();    
     llvm::cerr << size << ' ' << T.getWallTime() << '\n';
   }
   else {  
     llvm::cerr << '\n';    
-    RunGRSimpleVals(C, FD, *Ctx, Diags, Visualize);
+    RunGRSimpleVals(C, FD, *Ctx, Diags, Visualize, TrimGraph);
   }    
 }
 
index 3d2f9495fa0d7d9355210e9aa4af38b1320db2bd..63f769f06a39573ab65091a425e2f6b03f8f76be 100644 (file)
@@ -44,7 +44,7 @@ ASTConsumer *CreateUnitValsChecker(Diagnostic &Diags);
   
 ASTConsumer *CreateGRSimpleVals(Diagnostic &Diags,
                                 const std::string& Function,
-                                bool Visualize = false);
+                                bool Visualize = false, bool TrimGraph = false);
   
 ASTConsumer* CreateCFRefChecker(Diagnostic &Diags,
                                 const std::string& FunctionName); 
index 216e669ccca9bacd6d4b65379f3553ab2da3620c..680707591291dbe6eacee005c5dcfb6281569c4f 100644 (file)
@@ -461,6 +461,11 @@ static llvm::cl::opt<std::string>
 AnalyzeSpecificFunction("analyze-function",
                 llvm::cl::desc("Run analysis on specific function."));
 
+static llvm::cl::opt<bool>
+TrimGraph("trim-path-graph",
+      llvm::cl::desc("Only show error-related paths in the analysis graph."));
+
+
 //===----------------------------------------------------------------------===//
 // Target Triple Processing.
 //===----------------------------------------------------------------------===//
@@ -1039,7 +1044,7 @@ static ASTConsumer* CreateASTConsumer(const std::string& InFile,
       return CreateGRSimpleVals(Diag, AnalyzeSpecificFunction);
       
     case AnalysisGRSimpleValsView:
-      return CreateGRSimpleVals(Diag, AnalyzeSpecificFunction, true);
+      return CreateGRSimpleVals(Diag, AnalyzeSpecificFunction, true, TrimGraph);
       
     case CheckerCFRef:
       return CreateCFRefChecker(Diag, AnalyzeSpecificFunction);
index 81de2ac2e9d8e3d84b38ba17fb2857f47d8d9e4d..c8054ff253f2ab4d8fe1bcaffb9005460e485539 100644 (file)
@@ -25,7 +25,7 @@ namespace clang {
   ///  something more elaborate as the requirements on the interface become
   ///  clearer.  The value returned is the number of nodes in the ExplodedGraph.
   unsigned RunGRSimpleVals(CFG& cfg, FunctionDecl& FD, ASTContext& Ctx,
-                           Diagnostic& Diag, bool Visualize);
+                           Diagnostic& Diag, bool Visualize, bool TrimGraph);
   
 } // end clang namespace
 
index 65936399b3d748a876e6738e485340afc7d37786..b8c66368dabf655eaae8cd566a531b2946fbbf64 100644 (file)
@@ -55,6 +55,7 @@ protected:
     }
     
     void* getPtr() const {
+      assert (!getFlag());
       return reinterpret_cast<void*>(P & ~Mask);
     }
 
@@ -73,12 +74,13 @@ protected:
     
     unsigned size() const;
     
-    bool empty() const;
+    bool empty() const { return size() == 0; }
     
     void addNode(ExplodedNodeImpl* N);
     
     void setFlag() {
-      P |= AuxFlag;
+      assert (P == 0);
+      P = AuxFlag;
     }
     
     bool getFlag() const {
@@ -123,8 +125,8 @@ public:
   bool succ_empty() const { return Succs.empty(); }
   bool pred_empty() const { return Preds.size(); }
   
-  bool isSink() const { return Preds.getFlag(); }
-  void markAsSink() { Preds.setFlag(); }  
+  bool isSink() const { return Succs.getFlag(); }
+  void markAsSink() { Succs.setFlag(); }  
 };
 
 
@@ -232,6 +234,8 @@ protected:
   ///  is intended to be used only by GRCoreEngineImpl.
   virtual ExplodedNodeImpl* getNodeImpl(const ProgramPoint& L, void* State,
                                         bool* IsNew) = 0;
+  
+  virtual ExplodedGraphImpl* MakeEmptyGraph() const = 0;
 
   /// addRoot - Add an untyped node to the set of roots.
   ExplodedNodeImpl* addRoot(ExplodedNodeImpl* V) {
@@ -262,6 +266,10 @@ public:
   CFG& getCFG() { return cfg; }
   ASTContext& getContext() { return Ctx; }
   FunctionDecl& getFunctionDecl() { return FD; }
+  
+  ExplodedGraphImpl* Trim(ExplodedNodeImpl** NBeg,
+                          ExplodedNodeImpl** NEnd) const;
+  
 };
   
 template <typename CHECKER>
@@ -279,10 +287,15 @@ protected:
   AllNodesTy Nodes;
   
 protected:
-  virtual ExplodedNodeImpl*
-  getNodeImpl(const ProgramPoint& L, void* State, bool* IsNew) {
+  virtual ExplodedNodeImpl* getNodeImpl(const ProgramPoint& L,
+                                        void* State, bool* IsNew) {
+    
     return getNode(L, static_cast<StateTy*>(State), IsNew);
   }
+  
+  virtual ExplodedGraphImpl* MakeEmptyGraph() const {
+    return new ExplodedGraph(cfg, FD, Ctx);
+  }  
     
 public:
   ExplodedGraph(CFG& c, FunctionDecl& fd, ASTContext& ctx)
@@ -362,6 +375,22 @@ public:
   const_eop_iterator eop_end() const {
     return const_cast<ExplodedGraph>(this)->eop_end();
   }
+  
+  // Utility.
+  
+  ExplodedGraph* Trim(NodeTy** NBeg, NodeTy** NEnd) const {
+    
+    if (NBeg == NEnd)
+      return NULL;
+    
+    assert (NBeg < NEnd);
+    
+    ExplodedNodeImpl** NBegImpl = (ExplodedNodeImpl**) NBeg;
+    ExplodedNodeImpl** NEndImpl = (ExplodedNodeImpl**) NEnd;
+    
+    return static_cast<ExplodedGraph*>(ExplodedGraphImpl::Trim(NBegImpl,
+                                                               NEndImpl));
+  }
 };
   
   
index a1de93e8e27f16ec2a156e280b43884715970af3..520c3732c852aedb06c6b2cd13f3b0f71301d1e8 100644 (file)
@@ -149,7 +149,7 @@ public:
   
   /// ViewGraph - Visualize the ExplodedGraph created by executing the
   ///  simulation.
-  void ViewGraph();
+  void ViewGraph(bool trim = false);
   
   /// getInitialState - Return the initial state used for the root vertex
   ///  in the ExplodedGraph.
@@ -392,7 +392,8 @@ protected:
   /// VisitUnaryOperator - Transfer function logic for unary operators.
   void VisitUnaryOperator(UnaryOperator* B, NodeTy* Pred, NodeSet& Dst);
   
-  void VisitDeref(UnaryOperator* B, NodeTy* Pred, NodeSet& Dst);
+  void VisitDeref(UnaryOperator* U, NodeTy* Pred, NodeSet& Dst,
+                  bool GetLVal = false);
   
   RVal EvalCast(RVal X, QualType CastT) {
     if (X.isUnknownOrUndef())