From 8532ec3f4c8ef1019d8c46fc9351bd3085512063 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Wed, 20 Mar 2019 15:01:31 -0400
Subject: [PATCH 130/161] FIXME: WIP on interprocedural_path_summary

---
 gcc/diagnostic-path.h                              |   6 +
 gcc/diagnostic.c                                   |  20 +-
 gcc/pretty-print.c                                 |   5 +-
 .../gcc.dg/plugin/diagnostic_plugin_test_paths.c   |   2 +-
 gcc/tree-diagnostic.c                              | 288 ++++++++++++++++++---
 5 files changed, 260 insertions(+), 61 deletions(-)

diff --git a/gcc/diagnostic-path.h b/gcc/diagnostic-path.h
index a90c35b..4eea07e 100644
--- a/gcc/diagnostic-path.h
+++ b/gcc/diagnostic-path.h
@@ -120,6 +120,9 @@ class simple_diagnostic_event : public diagnostic_event
 class simple_diagnostic_path : public diagnostic_path
 {
  public:
+  simple_diagnostic_path (pretty_printer *event_pp)
+  : m_event_pp (event_pp) {}
+
   unsigned num_events () const FINAL OVERRIDE;
   const diagnostic_event & get_event (int idx) const FINAL OVERRIDE;
 
@@ -128,6 +131,9 @@ class simple_diagnostic_path : public diagnostic_path
 
  private:
   auto_delete_vec<simple_diagnostic_event> m_events;
+
+  /* (for use by add_event).  */
+  pretty_printer *m_event_pp;
 };
 
 // FIXME:
diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c
index be790b0..3f0dccc 100644
--- a/gcc/diagnostic.c
+++ b/gcc/diagnostic.c
@@ -1825,18 +1825,14 @@ simple_diagnostic_path::get_event (int idx) const
 }
 
 /* FIXME.  */
+// Use m_context's printer to format the events.
 
 void
-simple_diagnostic_path::add_event (location_t loc, tree fndecl, int depth, const char *fmt, ...)
+simple_diagnostic_path::add_event (location_t loc, tree fndecl, int depth,
+				   const char *fmt, ...)
 {
-  /* FIXME: use global_dc's pp.  */
-#if 1
-  pretty_printer *pp = global_dc->printer;
+  pretty_printer *pp = m_event_pp;
   pp_clear_output_area (pp);
-#else
-  pretty_printer tmp_pp;
-  pretty_printer *pp = &tmp_pp;
-#endif
 
   text_info ti;
   rich_location rich_loc (line_table, UNKNOWN_LOCATION);
@@ -1851,11 +1847,6 @@ simple_diagnostic_path::add_event (location_t loc, tree fndecl, int depth, const
   ti.x_data = NULL;
   ti.m_richloc = &rich_loc;
 
-#if 1
-#else
-  pp_show_color (pp) = pp_show_color (global_dc->printer);
-  pp_format_decoder (pp) = pp_format_decoder (global_dc->printer);
-#endif
   pp_format (pp, &ti);
   pp_output_formatted_text (pp);
 
@@ -1864,9 +1855,6 @@ simple_diagnostic_path::add_event (location_t loc, tree fndecl, int depth, const
   simple_diagnostic_event *new_event
     = new simple_diagnostic_event (loc, fndecl, depth, pp_formatted_text (pp));
   m_events.safe_push (new_event);
-#if 0
-  fprintf (stderr, "DBG: added to path %p: %s\n", this, new_event->m_desc);
-#endif
 
   pp_clear_output_area (pp);
 }
diff --git a/gcc/pretty-print.c b/gcc/pretty-print.c
index 2436b8a..686e7a5 100644
--- a/gcc/pretty-print.c
+++ b/gcc/pretty-print.c
@@ -1596,7 +1596,7 @@ pretty_printer::pretty_printer (const pretty_printer &other)
   indent_skip (other.indent_skip),
   wrapping (other.wrapping),
   format_decoder (other.format_decoder),
-  m_format_postprocessor (other.m_format_postprocessor), // FIXME: deep copy
+  m_format_postprocessor (NULL),
   emitted_prefix (other.emitted_prefix),
   need_newline (other.need_newline),
   translate_identifiers (other.translate_identifiers),
@@ -1607,6 +1607,9 @@ pretty_printer::pretty_printer (const pretty_printer &other)
   /* By default, we emit prefixes once per message.  */
   pp_prefixing_rule (this) = pp_prefixing_rule (&other);
   pp_set_prefix (this, NULL);
+
+  if (other.m_format_postprocessor)
+    m_format_postprocessor = other.m_format_postprocessor->clone ();
 }
 
 pretty_printer::~pretty_printer ()
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_paths.c b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_paths.c
index d813348..4a7c0be 100644
--- a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_paths.c
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_paths.c
@@ -124,7 +124,7 @@ pass_test_show_path::execute (function *fun)
     {
       auto_diagnostic_group d;
       gcc_rich_location richloc (gimple_location (call_to_PyList_Append));
-      diagnostic_path path;
+      simple_diagnostic_path path (global_dc->printer);
       path.add_event (gimple_location (call_to_PyList_New),
 		      fun->decl, 0,
 		      "when %qs fails, returning NULL",
diff --git a/gcc/tree-diagnostic.c b/gcc/tree-diagnostic.c
index d352ef8..2ec7720 100644
--- a/gcc/tree-diagnostic.c
+++ b/gcc/tree-diagnostic.c
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "diagnostic-path.h"
 #include "json.h"
 #include "gcc-rich-location.h"
+#include "diagnostic-color.h"
 #include "selftest.h"
 
 /* Prints out, if necessary, the name of the current function
@@ -368,10 +369,16 @@ can_consolidate_events (const diagnostic_event &e1,
   return true;
 }
 
-/* FIXME.  */
+/* A class for grouing together the events in a diagnostic_path into
+   ranges of events, partitioned by stack frame (i.e. by fndecl and
+   stack depth).
+
+   For use by print_path_summary.  */
 
 struct interprocedural_path_summary
 {
+  /* A range of consecutive events within a diagnostic_path,
+     all with the same fndecl and stack_depth.  */
   struct event_range
   {
     event_range (tree fndecl, int stack_depth, int start_idx, int end_idx)
@@ -388,7 +395,7 @@ struct interprocedural_path_summary
   auto_vec <event_range> m_ranges;
 };
 
-/* FIXME.  */
+/* interprocedural_path_summary's ctor.  */
 
 interprocedural_path_summary::
 interprocedural_path_summary (const diagnostic_path &path)
@@ -411,9 +418,6 @@ interprocedural_path_summary (const diagnostic_path &path)
 					     cur_start_idx, idx - 1));
 	  in_range = true;
 	  cur_start_idx = idx;
-	  // TODO: show the range of events
-	  // TODO: show ASCII art showing calls and returns
-	  // TODO: maybe show state changes?
 	}
       cur_fndecl = ev_fndecl;
       cur_stack_depth = ev_stack_depth;
@@ -439,12 +443,15 @@ write_indent (pretty_printer *pp, int spaces)
    capabilities of PP; -Wformat will complain.  */
 
 static void
-print_quoted_fndecl (pretty_printer *pp, tree fndecl)
+print_fndecl (pretty_printer *pp, tree fndecl, bool quoted)
 {
   const char *n = DECL_NAME (fndecl)
     ? identifier_to_locale (lang_hooks.decl_printable_name (fndecl, 2))
     : _("<anonymous>");
-  pp_printf (pp, "%qs", n);
+  if (quoted)
+    pp_printf (pp, "%qs", n);
+  else
+    pp_string (pp, n);
 }
 
 /* Print SUMMARY to PP, giving an overview of the interprocedural
@@ -483,21 +490,28 @@ print_path_summary (pretty_printer *pp,
     interprocedural_path_summary::event_range *range;
     FOR_EACH_VEC_ELT (summary.m_ranges, i, range)
       {
-	print_quoted_fndecl (tmp_pp, range->m_fndecl);
-	name_lengths.safe_push (strlen (pp_formatted_text (tmp_pp)));
+	print_fndecl (tmp_pp, range->m_fndecl, false);
+	/* We want the length of the quoted decl in characters, not in bytes.
+	   For now, we get the length of the unquoted decl (in bytes), hope
+	   it's ASCII, and add 2 for the quotes.  */
+	name_lengths.safe_push (strlen (pp_formatted_text (tmp_pp)) + 2);
 	pp_clear_output_area (tmp_pp);
       }
     delete tmp_pp;
   }
 
-#if 0
-  /* Determine indentation for each range.  */
-  auto_vec<int> per_range_vbar_column;
-  auto_vec<int> per_range_name_start_column;
-  // TODO: populate these, use them
-#endif
+  /* FIXME: do we want this to have its own name?  */
+  const char *const line_color = "note";
+  const char *start_line_color
+    = colorize_start (pp_show_color (pp), line_color);
+  const char *end_line_color = colorize_stop (pp_show_color (pp));
 
-  auto_vec<int> vbar_column_for_depth;
+  /* Keep track of column numbers of existing '|' characters for
+     stack depths we've already printed.  */
+  const int EMPTY = -1;
+  const int DELETED = -2;
+  typedef int_hash <int, EMPTY, DELETED> vbar_hash;
+  hash_map <vbar_hash, int> vbar_column_for_depth;
 
   /* Print the ranges.  */
   const int base_indent = 2;
@@ -506,7 +520,7 @@ print_path_summary (pretty_printer *pp,
   interprocedural_path_summary::event_range *range;
   FOR_EACH_VEC_ELT (summary.m_ranges, i, range)
     {
-      write_indent (pp, cur_indent); // FIXME
+      write_indent (pp, cur_indent);
       if (i > 0)
 	{
 	  const interprocedural_path_summary::event_range *prev_range
@@ -515,11 +529,13 @@ print_path_summary (pretty_printer *pp,
 	    {
 	      /* Show pushed stack frame(s).  */
 	      const char *push_prefix = "+--> ";
+	      pp_string (pp, start_line_color);
 	      pp_string (pp, push_prefix);
+	      pp_string (pp, end_line_color);
 	      cur_indent += strlen (push_prefix);
 	    }
 	}
-      print_quoted_fndecl (pp, range->m_fndecl);
+      print_fndecl (pp, range->m_fndecl, true);
       if (range->m_start_idx == range->m_end_idx)
 	pp_printf (pp, ": event %i",
 		   range->m_start_idx + 1);
@@ -537,9 +553,12 @@ print_path_summary (pretty_printer *pp,
 	  for (int event_idx = range->m_start_idx;
 	       event_idx <= range->m_end_idx; event_idx++)
 	    {
-	      write_indent (pp, cur_indent + midpoint); // FIXME
+	      write_indent (pp, cur_indent + midpoint);
 	      label_text event_desc = path->get_event (event_idx).get_desc ();
-	      pp_printf (pp, "| (%i): %s",
+	      pp_string (pp, start_line_color);
+	      pp_string (pp, "|");
+	      pp_string (pp, end_line_color);
+	      pp_printf (pp, " (%i): %s",
 			 event_idx + 1, event_desc.m_buffer);
 	      event_desc.maybe_free ();
 	      pp_newline (pp);
@@ -553,34 +572,54 @@ print_path_summary (pretty_printer *pp,
 
 	  if (!show_events)
 	    {
-	      write_indent (pp, cur_indent + midpoint); // FIXME
+	      write_indent (pp, cur_indent + midpoint);
+	      pp_string (pp, start_line_color);
 	      pp_printf (pp, "|");
+	      pp_string (pp, end_line_color);
 	      pp_newline (pp);
 	    }
 
 	  if (range->m_stack_depth > next_range->m_stack_depth)
 	    {
-	      // TODO: show returning from stack frame(s)
+	      /* Show returning from stack frame(s), by printing
+		 something like:
+		 "                   |\n"
+		 "     <------------ +\n"
+		 "     |\n".  */
+
 	      int next_midpoint = name_lengths[i + 1] / 2;
-	      int indent_for_next_frame = base_indent; // FIXME
-	      // FIXME: ^^^ should use vbar_column_for_depth
-	      write_indent (pp, indent_for_next_frame + next_midpoint); // FIXME
+
+	      gcc_assert (vbar_column_for_depth.get
+			  (next_range->m_stack_depth));
+
+	      int vbar_for_next_frame
+		= *vbar_column_for_depth.get (next_range->m_stack_depth);
+
+	      int indent_for_next_frame = vbar_for_next_frame - next_midpoint;
+	      write_indent (pp, vbar_for_next_frame);
+	      pp_string (pp, start_line_color);
 	      pp_character (pp, '<');
 	      for (int i = indent_for_next_frame + next_midpoint;
 		   i < cur_indent + midpoint - 1; i++)
 		pp_character (pp, '-');
 	      pp_character (pp, '+');
+	      pp_string (pp, end_line_color);
 	      pp_newline (pp);
 	      cur_indent = indent_for_next_frame;
 
-	      write_indent (pp, cur_indent + next_midpoint); // FIXME
+	      write_indent (pp, vbar_for_next_frame);
+	      pp_string (pp, start_line_color);
 	      pp_printf (pp, "|");
+	      pp_string (pp, end_line_color);
 	      pp_newline (pp);
 	    }
 	  else if (range->m_stack_depth < next_range->m_stack_depth)
 	    {
 	      /* Prepare to show pushed stack frame.  */
-	      vbar_column_for_depth.safe_push (cur_indent + midpoint);
+	      gcc_assert (range->m_stack_depth != EMPTY);
+	      gcc_assert (range->m_stack_depth != DELETED);
+	      vbar_column_for_depth.put (range->m_stack_depth,
+					 cur_indent + midpoint);
 	      cur_indent += midpoint;
 	    }
 
@@ -590,10 +629,7 @@ print_path_summary (pretty_printer *pp,
   // TODO: maybe show all events
   // TODO: color
 
-  // TODO: selftest for this!
   // TODO: DejaGnu test for this
-  // TODO: option for controlling this
-  /* TODO: example of recursion.  */
   /* TODO: example of recursion in DejaGnu tests.  */
 }
 
@@ -614,9 +650,13 @@ tree_print_diagnostic_path (diagnostic_context *context,
       inform (UNKNOWN_LOCATION, "summary of interprocedural path");
 
       interprocedural_path_summary summary (*path);
+
+      char *saved_prefix = pp_take_prefix (context->printer);
+      pp_set_prefix (context->printer, NULL);
       print_path_summary (context->printer,
 			  summary, path, true);
       pp_flush (context->printer);
+      pp_set_prefix (context->printer, saved_prefix);
     }
 
   /* TODO: options for different printing approaches.  */
@@ -740,9 +780,16 @@ tree_diagnostics_defaults (diagnostic_context *context)
 
 namespace selftest {
 
+/* FIXME.  */
+
 class test_diagnostic_path : public simple_diagnostic_path
 {
  public:
+  test_diagnostic_path (pretty_printer *event_pp)
+  : simple_diagnostic_path (event_pp)
+  {
+  }
+
   void add_entry (tree fndecl, int stack_depth)
   {
     add_event (UNKNOWN_LOCATION, fndecl, stack_depth,
@@ -766,9 +813,9 @@ class test_diagnostic_path : public simple_diagnostic_path
 /* FIXME.  */
 
 static void
-test_empty_path ()
+test_empty_path (pretty_printer *event_pp)
 {
-  simple_diagnostic_path path;
+  test_diagnostic_path path (event_pp);
   ASSERT_FALSE (path.interprocedural_p ());
 
   interprocedural_path_summary summary (path);
@@ -783,7 +830,42 @@ test_empty_path ()
 /* FIXME.  */
 
 static void
-test_interprocedural_path_1 ()
+test_intraprocedural_path (pretty_printer *event_pp)
+{
+  tree fntype_void_void
+    = build_function_type_array (void_type_node, 0, NULL);
+  tree fndecl_foo = build_fn_decl ("foo", fntype_void_void);
+
+  test_diagnostic_path path (event_pp);
+  path.add_event (UNKNOWN_LOCATION, fndecl_foo, 0, "first %qs", "free");
+  path.add_event (UNKNOWN_LOCATION, fndecl_foo, 0, "double %qs", "free");
+
+  ASSERT_FALSE (path.interprocedural_p ());
+
+  interprocedural_path_summary summary (path);
+  ASSERT_EQ (summary.m_ranges.length (), 1);
+
+  {
+    pretty_printer pp;
+    print_path_summary (&pp, summary, NULL, true);
+    ASSERT_STREQ ("  `foo': events 1-2 (depth 0)\n",
+		  pp_formatted_text (&pp));
+  }
+
+  {
+    pretty_printer pp;
+    print_path_summary (&pp, summary, &path, true);
+    ASSERT_STREQ ("  `foo': events 1-2 (depth 0)\n"
+		  "    | (1): first `free'\n"
+		  "    | (2): double `free'\n",
+		  pp_formatted_text (&pp));
+  }
+}
+
+/* FIXME.  */
+
+static void
+test_interprocedural_path_1 (pretty_printer *event_pp)
 {
   /* Build fndecls.  The types aren't quite right, but that
      doesn't matter for the purposes of this test.  */
@@ -799,7 +881,7 @@ test_interprocedural_path_1 ()
   tree fndecl_wrapped_free
     = build_fn_decl ("wrapped_free", fntype_void_void);
 
-  test_diagnostic_path path;
+  test_diagnostic_path path (event_pp);
   path.add_entry (fndecl_test, 0);
   path.add_call (fndecl_test, 0, fndecl_make_boxed_int);
   path.add_call (fndecl_make_boxed_int, 1, fndecl_wrapped_malloc);
@@ -889,21 +971,141 @@ test_interprocedural_path_1 ()
   }
 }
 
+/* Example where we pop the stack to an intermediate frame, rather than the
+   initial one.  */
+
+static void
+test_interprocedural_path_2 (pretty_printer *event_pp)
+{
+  /* Build fndecls.  The types aren't quite right, but that
+     doesn't matter for the purposes of this test.  */
+  tree fntype_void_void
+    = build_function_type_array (void_type_node, 0, NULL);
+  tree fndecl_foo = build_fn_decl ("foo", fntype_void_void);
+  tree fndecl_bar = build_fn_decl ("bar", fntype_void_void);
+  tree fndecl_baz = build_fn_decl ("baz", fntype_void_void);
+
+  test_diagnostic_path path (event_pp);
+  path.add_entry (fndecl_foo, 0);
+  path.add_call (fndecl_foo, 0, fndecl_bar);
+  path.add_call (fndecl_bar, 1, fndecl_baz);
+  path.add_return (fndecl_bar, 1);
+  path.add_call (fndecl_bar, 1, fndecl_baz);
+  ASSERT_EQ (path.num_events (), 8);
+
+  ASSERT_TRUE (path.interprocedural_p ());
+
+  interprocedural_path_summary summary (path);
+  ASSERT_EQ (summary.m_ranges.length (), 5);
+
+  {
+    pretty_printer pp;
+    print_path_summary (&pp, summary, NULL, true);
+    ASSERT_STREQ
+      ("  `foo': events 1-2 (depth 0)\n"
+       "    |\n"
+       "    +--> `bar': events 3-4 (depth 1)\n"
+       "           |\n"
+       "           +--> `baz': event 5 (depth 2)\n"
+       "                  |\n"
+       "           <------+\n"
+       "           |\n"
+       "         `bar': events 6-7 (depth 1)\n"
+       "           |\n"
+       "           +--> `baz': event 8 (depth 2)\n",
+       pp_formatted_text (&pp));
+  }
+
+  {
+    pretty_printer pp;
+    print_path_summary (&pp, summary, &path, true);
+    ASSERT_STREQ
+      ("  `foo': events 1-2 (depth 0)\n"
+       "    | (1): entering `foo'\n"
+       "    | (2): calling `bar'\n"
+       "    +--> `bar': events 3-4 (depth 1)\n"
+       "           | (3): entering `bar'\n"
+       "           | (4): calling `baz'\n"
+       "           +--> `baz': event 5 (depth 2)\n"
+       "                  | (5): entering `baz'\n"
+       "           <------+\n"
+       "           |\n"
+       "         `bar': events 6-7 (depth 1)\n"
+       "           | (6): returning to `bar'\n"
+       "           | (7): calling `baz'\n"
+       "           +--> `baz': event 8 (depth 2)\n"
+       "                  | (8): entering `baz'\n",
+       pp_formatted_text (&pp));
+  }
+}
+
+/* FIXME.  */
+static void
+test_recursion (pretty_printer *event_pp)
+{
+  tree fntype_void_void
+    = build_function_type_array (void_type_node, 0, NULL);
+  tree fndecl_factorial = build_fn_decl ("factorial", fntype_void_void);
+
+ test_diagnostic_path path (event_pp);
+  path.add_entry (fndecl_factorial, 0);
+  for (int depth = 0; depth < 3; depth++)
+    path.add_call (fndecl_factorial, depth, fndecl_factorial);
+  ASSERT_EQ (path.num_events (), 7);
+
+  ASSERT_TRUE (path.interprocedural_p ());
+
+  interprocedural_path_summary summary (path);
+  ASSERT_EQ (summary.m_ranges.length (), 4);
+
+  {
+    pretty_printer pp;
+    print_path_summary (&pp, summary, NULL, true);
+    ASSERT_STREQ
+      ("  `factorial': events 1-2 (depth 0)\n"
+       "       |\n"
+       "       +--> `factorial': events 3-4 (depth 1)\n"
+       "                 |\n"
+       "                 +--> `factorial': events 5-6 (depth 2)\n"
+       "                           |\n"
+       "                           +--> `factorial': event 7 (depth 3)\n",
+       pp_formatted_text (&pp));
+  }
+
+  {
+    pretty_printer pp;
+    print_path_summary (&pp, summary, &path, true);
+    ASSERT_STREQ
+      ("  `factorial': events 1-2 (depth 0)\n"
+       "       | (1): entering `factorial'\n"
+       "       | (2): calling `factorial'\n"
+       "       +--> `factorial': events 3-4 (depth 1)\n"
+       "                 | (3): entering `factorial'\n"
+       "                 | (4): calling `factorial'\n"
+       "                 +--> `factorial': events 5-6 (depth 2)\n"
+       "                           | (5): entering `factorial'\n"
+       "                           | (6): calling `factorial'\n"
+       "                           +--> `factorial': event 7 (depth 3)\n"
+       "                                     | (7): entering `factorial'\n",
+       pp_formatted_text (&pp));
+  }
+
+}
+
 /* Run all of the selftests within this file.  */
 
 void
 tree_diagnostic_c_tests ()
 {
   auto_fix_quotes fix_quotes;
-  test_empty_path ();
-#if 0
-  test_intraprocedural_path ();
-#endif
-  test_interprocedural_path_1 ();
-#if 0
-  test_interprocedural_path_2 ();
-  test_recursion ();
-#endif
+  pretty_printer *event_pp = global_dc->printer->clone ();
+  pp_show_color (event_pp) = 0;
+  test_empty_path (event_pp);
+  test_intraprocedural_path (event_pp);
+  test_interprocedural_path_1 (event_pp);
+  test_interprocedural_path_2 (event_pp);
+  test_recursion (event_pp);
+  delete event_pp;
 }
 
 } // namespace selftest
-- 
1.8.5.3

