]> granicus.if.org Git - clang/commitdiff
static analyzer: Further reduce the analyzer's memory usage when analyzing sqlite3...
authorTed Kremenek <kremenek@apple.com>
Wed, 9 Feb 2011 01:27:33 +0000 (01:27 +0000)
committerTed Kremenek <kremenek@apple.com>
Wed, 9 Feb 2011 01:27:33 +0000 (01:27 +0000)
The optimization involves eagerly pruning ExplodedNodes from the ExplodedGraph that contain
practically no difference between the predecessor and successor nodes.  For example, if
the state is different between a predecessor and a node, the node is left in.  Only for
the 'environment' component of the state do we not care if the ExplodedNodes are different.
This paves the way for future optimizations where we can reclaim the environment objects.

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

include/clang/Driver/CC1Options.td
include/clang/Frontend/AnalyzerOptions.h
include/clang/StaticAnalyzer/PathSensitive/AnalysisManager.h
include/clang/StaticAnalyzer/PathSensitive/ExplodedGraph.h
include/clang/StaticAnalyzer/PathSensitive/GRState.h
lib/Frontend/CompilerInvocation.cpp
lib/StaticAnalyzer/Checkers/AnalysisConsumer.cpp
lib/StaticAnalyzer/Checkers/ExprEngine.cpp
lib/StaticAnalyzer/Core/CoreEngine.cpp
lib/StaticAnalyzer/Core/ExplodedGraph.cpp

index 813ee5bcc37ca9609107ff18d0303ec99acf0a74..097a0301e1401d0a9778aeba31ec8390ddc51067 100644 (file)
@@ -107,6 +107,8 @@ def analyzer_eagerly_assume : Flag<"-analyzer-eagerly-assume">,
   HelpText<"Eagerly assume the truth/falseness of some symbolic constraints">;
 def analyzer_no_purge_dead : Flag<"-analyzer-no-purge-dead">,
   HelpText<"Don't remove dead symbols, bindings, and constraints before processing a statement">;
+def analyzer_no_eagerly_trim_egraph : Flag<"-analyzer-no-eagerly-trim-egraph">,
+  HelpText<"Don't eagerly remove uninteresting ExplodedNodes from the ExplodedGraph">;
 def trim_egraph : Flag<"-trim-egraph">,
   HelpText<"Only show error-related paths in the analysis graph">;
 def analyzer_viz_egraph_graphviz : Flag<"-analyzer-viz-egraph-graphviz">,
index 8704fff687357c616e9fca47fc786a589da5d919..32075c760c9a82a708c38c79c47c035449832380 100644 (file)
@@ -80,6 +80,7 @@ public:
   unsigned UnoptimizedCFG : 1;
   unsigned CFGAddImplicitDtors : 1;
   unsigned CFGAddInitializers : 1;
+  unsigned EagerlyTrimEGraph : 1;
 
 public:
   AnalyzerOptions() {
@@ -103,6 +104,7 @@ public:
     UnoptimizedCFG = 0;
     CFGAddImplicitDtors = 0;
     CFGAddInitializers = 0;
+    EagerlyTrimEGraph = 0;
   }
 };
 
index ba86ed8ba2e39f26ebe0aef15c7495a63ef256dc..2ecb19e5271a7078d23253ca2b70328c10a61df3 100644 (file)
@@ -69,6 +69,7 @@ class AnalysisManager : public BugReporterData {
   bool EagerlyAssume;
   bool TrimGraph;
   bool InlineCall;
+  bool EagerlyTrimEGraph;
 
 public:
   AnalysisManager(ASTContext &ctx, Diagnostic &diags, 
@@ -79,14 +80,16 @@ public:
                   unsigned maxnodes, unsigned maxvisit,
                   bool vizdot, bool vizubi, bool purge, bool eager, bool trim,
                   bool inlinecall, bool useUnoptimizedCFG,
-                  bool addImplicitDtors, bool addInitializers)
+                  bool addImplicitDtors, bool addInitializers,
+                  bool eagerlyTrimEGraph)
 
     : AnaCtxMgr(useUnoptimizedCFG, addImplicitDtors, addInitializers),
       Ctx(ctx), Diags(diags), LangInfo(lang), PD(pd),
       CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr),Idxer(idxer),
       AScope(ScopeDecl), MaxNodes(maxnodes), MaxVisit(maxvisit),
       VisualizeEGDot(vizdot), VisualizeEGUbi(vizubi), PurgeDead(purge),
-      EagerlyAssume(eager), TrimGraph(trim), InlineCall(inlinecall) {}
+      EagerlyAssume(eager), TrimGraph(trim), InlineCall(inlinecall),
+      EagerlyTrimEGraph(eagerlyTrimEGraph) {}
   
   ~AnalysisManager() { FlushDiagnostics(); }
   
@@ -146,6 +149,8 @@ public:
     return VisualizeEGDot || VisualizeEGUbi;
   }
 
+  bool shouldEagerlyTrimExplodedGraph() const { return EagerlyTrimEGraph; }
+
   bool shouldTrimGraph() const { return TrimGraph; }
 
   bool shouldPurgeDead() const { return PurgeDead; }
index 0d6044b5d2bfbdd4487b8c653566edd8d21586cb..8c65608c7fe793201effd00afba7fca4dce39cb2 100644 (file)
@@ -75,7 +75,7 @@ class ExplodedNode : public llvm::FoldingSetNode {
     ExplodedNode *getNode() const {
       return reinterpret_cast<ExplodedNode*>(getPtr());
     }
-
+    
   public:
     NodeGroup() : P(0) {}
 
@@ -89,6 +89,8 @@ class ExplodedNode : public llvm::FoldingSetNode {
 
     void addNode(ExplodedNode* N, ExplodedGraph &G);
 
+    void replaceNode(ExplodedNode *node);
+
     void setFlag() {
       assert(P == 0);
       P = AuxFlag;
@@ -208,6 +210,10 @@ public:
   };
 
   static void SetAuditor(Auditor* A);
+  
+private:
+  void replaceSuccessor(ExplodedNode *node) { Succs.replaceNode(node); }
+  void replacePredecessor(ExplodedNode *node) { Preds.replaceNode(node); }
 };
 
 // FIXME: Is this class necessary?
@@ -249,6 +255,15 @@ protected:
 
   /// NumNodes - The number of nodes in the graph.
   unsigned NumNodes;
+  
+  /// A list of recently allocated nodes that can potentially be recycled.
+  void *recentlyAllocatedNodes;
+  
+  /// A list of nodes that can be reused.
+  void *freeNodes;
+  
+  /// A flag that indicates whether nodes should be recycled.
+  bool reclaimNodes;
 
 public:
   /// getNode - Retrieve the node associated with a (Location,State) pair,
@@ -275,10 +290,10 @@ public:
     return V;
   }
 
-  ExplodedGraph() : NumNodes(0) {}
-
-  ~ExplodedGraph() {}
+  ExplodedGraph() : NumNodes(0), recentlyAllocatedNodes(0), freeNodes(0) {}
 
+  ~ExplodedGraph();
+  
   unsigned num_roots() const { return Roots.size(); }
   unsigned num_eops() const { return EndNodes.size(); }
 
@@ -332,6 +347,14 @@ public:
                               const ExplodedNode* const * NEnd,
                               InterExplodedGraphMap *M,
                     llvm::DenseMap<const void*, const void*> *InverseMap) const;
+
+  /// Enable tracking of recently allocated nodes for potential reclamation
+  /// when calling reclaimRecentlyAllocatedNodes().
+  void enableNodeReclamation() { reclaimNodes = true; }
+
+  /// Reclaim "uninteresting" nodes created since the last time this method
+  /// was called.
+  void reclaimRecentlyAllocatedNodes();
 };
 
 class ExplodedNodeSet {
index 5a87cb4fb701de71728e6e975fbc89364e2d957f..d06ffd644a29cff969d99f83feab49c4256925c6 100644 (file)
@@ -78,6 +78,7 @@ private:
   void operator=(const GRState& R) const; // Do not implement.
 
   friend class GRStateManager;
+  friend class ExplodedGraph;
 
   llvm::PointerIntPair<GRStateManager *, 1, bool> stateMgr;
   Environment Env;           // Maps a Stmt to its current SVal.
index e90cf8ddd2f6319796112bf162a3c731c664c8dc..6fc73738223526bc26a7f6563f884983f58abe65 100644 (file)
@@ -878,6 +878,7 @@ static void ParseAnalyzerArgs(AnalyzerOptions &Opts, ArgList &Args,
   Opts.TrimGraph = Args.hasArg(OPT_trim_egraph);
   Opts.MaxNodes = Args.getLastArgIntValue(OPT_analyzer_max_nodes, 150000,Diags);
   Opts.MaxLoop = Args.getLastArgIntValue(OPT_analyzer_max_loop, 4, Diags);
+  Opts.EagerlyTrimEGraph = !Args.hasArg(OPT_analyzer_no_eagerly_trim_egraph);
   Opts.InlineCall = Args.hasArg(OPT_analyzer_inline_call);
   Opts.IdempotentOps = Args.hasArg(OPT_analysis_WarnIdempotentOps);
   Opts.ObjCSelfInitCheck = Args.hasArg(OPT_analysis_WarnObjCSelfInit);
index a20f995639d21e8d77aac1f7513d8c0b1c2edf0f..2b210a880479b12930e08a279e0a74676fe5ef1f 100644 (file)
@@ -194,7 +194,8 @@ public:
                                   Opts.PurgeDead, Opts.EagerlyAssume,
                                   Opts.TrimGraph, Opts.InlineCall,
                                   Opts.UnoptimizedCFG, Opts.CFGAddImplicitDtors,
-                                  Opts.CFGAddInitializers));
+                                  Opts.CFGAddInitializers,
+                                  Opts.EagerlyTrimEGraph));
   }
 
   virtual void HandleTranslationUnit(ASTContext &C);
index cadbca6b3d0ea2032a8d83f6f3dfc7627163bda1..e2ad17e590b3d3e1e1ccd1222d502f741c5e5874 100644 (file)
@@ -338,6 +338,11 @@ ExprEngine::ExprEngine(AnalysisManager &mgr, TransferFuncs *tf)
   // FIXME: Eventually remove the TF object entirely.
   TF->RegisterChecks(*this);
   TF->RegisterPrinters(getStateManager().Printers);
+  
+  if (mgr.shouldEagerlyTrimExplodedGraph()) {
+    // Enable eager node reclaimation when constructing the ExplodedGraph.  
+    G.enableNodeReclamation();
+  }
 }
 
 ExprEngine::~ExprEngine() {
@@ -573,6 +578,8 @@ void ExprEngine::processCFGElement(const CFGElement E,
 }
 
 void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) {
+  // Reclaim any unnecessary nodes in the ExplodedGraph.
+  G.reclaimRecentlyAllocatedNodes();
   // Recycle any unused states in the GRStateManager.
   StateMgr.recycleUnusedStates();
   
index 13cca35aacfa5b4fce3843d23646d777c9eaa4f3..69aa664f9c1033e78cfd47558e46f4f23e54a2fb 100644 (file)
@@ -783,7 +783,8 @@ void CallEnterNodeBuilder::generateNode(const GRState *state) {
                          OldMgr.shouldInlineCall(),
                      OldMgr.getAnalysisContextManager().getUseUnoptimizedCFG(),
                      OldMgr.getAnalysisContextManager().getAddImplicitDtors(),
-                     OldMgr.getAnalysisContextManager().getAddInitializers());
+                     OldMgr.getAnalysisContextManager().getAddInitializers(),
+                     OldMgr.shouldEagerlyTrimExplodedGraph());
     llvm::OwningPtr<TransferFuncs> TF(MakeCFRefCountTF(AMgr.getASTContext(),
                                                          /* GCEnabled */ false,
                                                         AMgr.getLangOptions()));
index 4c4612fab5e760793e916dc26dbf1fd7e6f2e90a..9e8566056f4f3c036c563fac87a8359bd242110e 100644 (file)
@@ -40,6 +40,94 @@ void ExplodedNode::SetAuditor(ExplodedNode::Auditor* A) {
 #endif
 }
 
+//===----------------------------------------------------------------------===//
+// Cleanup.
+//===----------------------------------------------------------------------===//
+
+typedef std::vector<ExplodedNode*> NodeList;
+static inline NodeList*& getNodeList(void *&p) { return (NodeList*&) p; }
+
+ExplodedGraph::~ExplodedGraph() {
+  if (reclaimNodes) {
+    delete getNodeList(recentlyAllocatedNodes);
+    delete getNodeList(freeNodes);
+  }
+}
+
+//===----------------------------------------------------------------------===//
+// Node reclamation.
+//===----------------------------------------------------------------------===//
+
+void ExplodedGraph::reclaimRecentlyAllocatedNodes() {
+  if (!recentlyAllocatedNodes)
+    return;
+  NodeList &nl = *getNodeList(recentlyAllocatedNodes);
+  // Reclaimn all nodes that match *all* the following criteria:
+  //
+  // (1) 1 predecessor (that has one successor)
+  // (2) 1 successor (that has one predecessor)
+  // (3) The ProgramPoint is for a PostStmt.
+  // (4) There is no 'tag' for the ProgramPoint.
+  // (5) The 'store' is the same as the predecessor.
+  // (6) The 'GDM' is the same as the predecessor.
+  // (7) The LocationContext is the same as the predecessor.
+  // (8) The PostStmt is for a non-CFGElement expression.
+  
+  for (NodeList::iterator i = nl.begin(), e = nl.end() ; i != e; ++i) {
+    ExplodedNode *node = *i;
+    
+    // Conditions 1 and 2.
+    if (node->pred_size() != 1 || node->succ_size() != 1)
+      continue;
+
+    ExplodedNode *pred = *(node->pred_begin());
+    if (pred->succ_size() != 1)
+      continue;
+
+    ExplodedNode *succ = *(node->succ_begin());
+    if (succ->pred_size() != 1)
+      continue;
+
+    // Condition 3.
+    ProgramPoint progPoint = node->getLocation();
+    if (!isa<PostStmt>(progPoint))
+      continue;
+
+    // Condition 4.
+    PostStmt ps = cast<PostStmt>(progPoint);
+    if (ps.getTag() || isa<PostStmtCustom>(ps))
+      continue;
+
+    if (isa<BinaryOperator>(ps.getStmt()))
+      continue;
+
+    // Conditions 5, 6, and 7.
+    const GRState *state = node->getState();
+    const GRState *pred_state = pred->getState();    
+    if (state->St != pred_state->St || state->GDM != pred_state->GDM ||
+        progPoint.getLocationContext() != pred->getLocationContext())
+      continue;
+
+    // Condition 8.
+    if (node->getCFG().isBlkExpr(ps.getStmt()))
+      continue;
+    
+    // If we reach here, we can remove the node.  This means:
+    // (a) changing the predecessors successor to the successor of this node
+    // (b) changing the successors predecessor to the predecessor of this node
+    // (c) Putting 'node' onto freeNodes.
+    pred->replaceSuccessor(succ);
+    succ->replacePredecessor(pred);
+    if (!freeNodes)
+      freeNodes = new NodeList();
+    getNodeList(freeNodes)->push_back(node);
+    Nodes.RemoveNode(node);
+  }
+  
+  nl.clear();
+}
+
 //===----------------------------------------------------------------------===//
 // ExplodedNode.
 //===----------------------------------------------------------------------===//
@@ -57,6 +145,12 @@ void ExplodedNode::addPredecessor(ExplodedNode* V, ExplodedGraph &G) {
 #endif
 }
 
+void ExplodedNode::NodeGroup::replaceNode(ExplodedNode *node) {
+  assert(getKind() == Size1);
+  P = reinterpret_cast<uintptr_t>(node);
+  assert(getKind() == Size1);
+}
+
 void ExplodedNode::NodeGroup::addNode(ExplodedNode* N, ExplodedGraph &G) {
   assert((reinterpret_cast<uintptr_t>(N) & Mask) == 0x0);
   assert(!getFlag());
@@ -129,10 +223,24 @@ ExplodedNode *ExplodedGraph::getNode(const ProgramPoint& L,
   NodeTy* V = Nodes.FindNodeOrInsertPos(profile, InsertPos);
 
   if (!V) {
-    // Allocate a new node.
-    V = (NodeTy*) getAllocator().Allocate<NodeTy>();
+    if (freeNodes && !getNodeList(freeNodes)->empty()) {
+      NodeList *nl = getNodeList(freeNodes);
+      V = nl->back();
+      nl->pop_back();
+    }
+    else {
+      // Allocate a new node.
+      V = (NodeTy*) getAllocator().Allocate<NodeTy>();
+    }
+
     new (V) NodeTy(L, State);
 
+    if (reclaimNodes) {
+      if (!recentlyAllocatedNodes)
+        recentlyAllocatedNodes = new NodeList();
+      getNodeList(recentlyAllocatedNodes)->push_back(V);
+    }
+
     // Insert the node into the node set and return it.
     Nodes.InsertNode(V, InsertPos);