From 95a45a53509fa9cfcdcccce353cd132a9b9ad567 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Wed, 6 May 2020 17:40:44 -0400
Subject: [PATCH 103/159] FIXME: WIP on leaks, trying to get at all ssa names

---
 gcc/analyzer/engine.cc        | 10 ++++
 gcc/analyzer/program-state.cc | 62 +++++++++++++++++++++++
 gcc/analyzer/program-state.h  |  4 ++
 gcc/analyzer/region-model2.cc | 95 +++++++++++++++++++++++++++++++++++
 gcc/analyzer/region-model2.h  |  1 +
 5 files changed, 172 insertions(+)

diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index c3af9e1059c..d2e656249a4 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -2560,6 +2560,16 @@ exploded_graph::add_edge (exploded_node *src, exploded_node *dest,
   exploded_edge *e = new exploded_edge (src, dest, m_ext_state,
 					sedge, change, custom_info);
   digraph<eg_traits>::add_edge (e);
+
+  // TODO: detect leaks
+  if (dest->get_state ().m_region_model2)
+    {
+      // FIXME:
+      program_state::detect_leaks2 (src->get_state (),
+				    dest->get_state (),
+				    m_ext_state);
+    }
+  
   return e;
 }
 
diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc
index 7a28b2cc0b8..c16bdcaab0a 100644
--- a/gcc/analyzer/program-state.cc
+++ b/gcc/analyzer/program-state.cc
@@ -1859,6 +1859,68 @@ program_state::validate (const extrinsic_state &ext_state) const
     }
 }
 
+static void
+log_set_of_svalue2s (logger *logger, const char *name,
+		     const hash_set<const svalue2 *> &set)
+{
+  logger->log (name);
+  logger->inc_indent ();
+  for (hash_set<const svalue2 *>::iterator iter = set.begin ();
+       iter != set.end (); ++iter)
+    {
+      logger->start_log_line ();
+      pretty_printer *pp = logger->get_printer ();
+      const svalue2 *sval = (*iter);
+      pp_pointer (pp, sval);
+      pp_string (pp, ": ");
+      sval->dump_to_pp (pp, false);
+      logger->end_log_line ();
+    }
+  logger->dec_indent ();
+}
+
+/* FIXME.  */
+
+void
+program_state::detect_leaks2 (const program_state &src_state,
+			      const program_state &dest_state,
+			      const extrinsic_state &ext_state)
+{
+  logger *logger = ext_state.get_logger ();
+  LOG_SCOPE (logger);
+
+  /* Get svalues reachable from each of src_state and dst_state.  */
+  hash_set<const svalue2 *> src_svalues;
+  hash_set<const svalue2 *> dest_svalues;
+  src_state.m_region_model2->get_reachable_svalues (&src_svalues);
+  dest_state.m_region_model2->get_reachable_svalues (&dest_svalues);
+
+  if (logger)
+    {
+      log_set_of_svalue2s (logger, "src_state reachable svalues:", src_svalues);
+      log_set_of_svalue2s (logger, "dest_state reachable svalues:",
+			   dest_svalues);
+    }
+
+  for (hash_set<const svalue2 *>::iterator iter = src_svalues.begin ();
+       iter != src_svalues.end (); ++iter)
+    {
+      const svalue2 *sval = (*iter);
+      if (!dest_svalues.contains (sval))
+	{
+	  pretty_printer pp1, pp2;
+	  pp_format_decoder (&pp1) = default_tree_printer;
+	  pp_format_decoder (&pp2) = default_tree_printer;
+	  sval->dump_to_pp (&pp1, true);
+	  sval->dump_to_pp (&pp2, false);
+	  error ("possible leak: %qs %qs",
+		 pp_formatted_text (&pp1),
+		 pp_formatted_text (&pp2));
+	  //gcc_unreachable (); // TODO
+	}
+    }
+}
+
 /* Dump this sm_change to PP.  */
 
 void
diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h
index 513f470f585..00023293a74 100644
--- a/gcc/analyzer/program-state.h
+++ b/gcc/analyzer/program-state.h
@@ -409,6 +409,10 @@ public:
 
   void validate (const extrinsic_state &ext_state) const;
 
+  static void detect_leaks2 (const program_state &src_state,
+			     const program_state &dest_state,
+			     const extrinsic_state &ext_state);
+
   revamp_state *m_revamp_state;
   region_model2 *m_region_model2;
   /* TODO: lose the pointer here (const-correctness issues?).  */
diff --git a/gcc/analyzer/region-model2.cc b/gcc/analyzer/region-model2.cc
index c7fe5f2e458..ecc0d68e434 100644
--- a/gcc/analyzer/region-model2.cc
+++ b/gcc/analyzer/region-model2.cc
@@ -60,6 +60,9 @@ along with GCC; see the file COPYING3.  If not see
 #include "analyzer/pending-diagnostic.h"
 #include "analyzer/analyzer-selftests.h"
 #include "stor-layout.h"
+#include "value-range.h" // for tree-ssanames.h
+#include "gimple-ssa.h" // for struct gimple_df in FOR_EACH_SSA_NAME
+#include "tree-ssanames.h" // for FOR_EACH_SSA_NAME
 
 #if ENABLE_ANALYZER
 
@@ -4274,6 +4277,16 @@ public:
       }
   }
 
+  /* Iteration over reachable regions.  */
+  hash_set<const region2 *>::iterator begin ()
+  {
+    return m_reachable_regs.begin ();
+  }
+  hash_set<const region2 *>::iterator end ()
+  {
+    return m_reachable_regs.end ();
+  }
+
   void dump_to_pp (pretty_printer *pp) const
   {
     pp_string (pp, "reachable clusters: ");
@@ -4396,6 +4409,88 @@ region_model2::handle_unrecognized_call (const gcall *call,
 #endif
 }
 
+/* FIXME.  */
+
+static void
+add_svalues_to_set_cb (const svalue2 *sval, hash_set<const svalue2 *> *out)
+{
+  out->add (sval);
+}
+
+/* FIXME.
+   Traverse the region2s in this model, determining what region2s are
+   reachable from the store.
+
+   Find svalues that haven't leaked.    */
+
+void
+region_model2::get_reachable_svalues (hash_set<const svalue2 *> *out)
+{
+  reachable_region2s reachable_regions (&m_store);
+
+  /* Add globals and regions that already escaped in previous
+     unknown calls.  */
+  m_store.for_each_cluster (reachable_region2s::init_cluster_cb,
+			    &reachable_regions);
+
+  /* Mark all locals in all frames (and what's reachable from them).  */
+#if 1
+  for (const frame_region2 *iter_frame = get_current_frame ();
+       iter_frame; iter_frame = iter_frame->get_calling_frame ())
+    {
+      region_model2_context *ctxt = NULL; // FIXME
+      unsigned i;
+      tree name;
+      FOR_EACH_SSA_NAME (i, name, iter_frame->get_function ())
+	{
+	  /* Ignore the ".MEM" SSA names.  */
+	  if (tree var = SSA_NAME_VAR (name))
+	    if (TREE_CODE (var) == VAR_DECL)
+	      if (VAR_DECL_IS_VIRTUAL_OPERAND (var))
+		continue;
+	  const region2 *name_reg
+	    = iter_frame->get_region_for_local (m_mgr, name, ctxt);
+	  reachable_regions.add (name_reg, false);
+	}
+      //#error
+      /* FIXME: handle both initial values (but not for uninit values)
+	 and explicitly bound values.  */
+      /* FIXME: we need to get at all live SSA names in the frame
+	 that might have values; is there a way to do this?  */
+      // (and all parms/locals)
+    }
+#else
+  for (store2::cluster_map_t::iterator iter = m_store.begin ();
+       iter != m_store.end (); ++iter)
+    {
+      const region2 *base_reg = (*iter).first;
+      if (const region2 *parent = base_reg->get_parent_region ())
+	if (parent->get_kind () == region2::RK_FRAME)
+	  reachable_regions.add (base_reg, false);
+    }
+  /* TODO: we also need to count the regions for live SSA names
+     (and params) at the current program point, for those that
+     still have their initial values (but not for uninit values).  */
+#endif
+
+  /* Populate *OUT based on the clusters that were reachable.  */
+  for (hash_set<const region2 *>::iterator iter = reachable_regions.begin ();
+       iter != reachable_regions.end (); ++iter)
+    {
+      const region2 *base_reg = (*iter);
+      binding_cluster2 *cluster = m_store.get_cluster (base_reg);
+      if (cluster)
+	cluster->for_each_value (add_svalues_to_set_cb, out);
+      else
+	{
+	  /* If not explicitly bound (or escaped), add initial svalue.
+	     FIXME: what about uninit?  */
+	  const svalue2 *init = m_mgr->get_or_create_initial_value (base_reg);
+	  out->add (init);
+	}
+    }
+}
+
 /* Update this model for the RETURN_STMT, using CTXT to report any
    diagnostics.  */
 
diff --git a/gcc/analyzer/region-model2.h b/gcc/analyzer/region-model2.h
index a9bf778e2d3..ddf8109ec0c 100644
--- a/gcc/analyzer/region-model2.h
+++ b/gcc/analyzer/region-model2.h
@@ -1756,6 +1756,7 @@ class region_model2
 #endif
   void handle_unrecognized_call (const gcall *call,
 				 region_model2_context *ctxt);
+  void get_reachable_svalues (hash_set<const svalue2 *> *out);
   bool on_greturn (const supernode *snode,
 		   const greturn *stmt);
   void on_setjmp (const gcall *stmt, const exploded_node *enode,
-- 
2.21.0

