1 /* Paths through the code associated with a diagnostic.
2    Copyright (C) 2019-2022 Free Software Foundation, Inc.
3    Contributed by David Malcolm <dmalcolm@redhat.com>
4 
5 This file is part of GCC.
6 
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 3, or (at your option) any later
10 version.
11 
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3.  If not see
19 <http://www.gnu.org/licenses/>.  */
20 
21 #include "config.h"
22 #include "system.h"
23 #include "coretypes.h"
24 #include "tree.h"
25 #include "diagnostic.h"
26 #include "tree-pretty-print.h"
27 #include "gimple-pretty-print.h"
28 #include "tree-diagnostic.h"
29 #include "langhooks.h"
30 #include "intl.h"
31 #include "diagnostic-path.h"
32 #include "json.h"
33 #include "gcc-rich-location.h"
34 #include "diagnostic-color.h"
35 #include "diagnostic-event-id.h"
36 #include "selftest.h"
37 #include "selftest-diagnostic.h"
38 
39 /* Anonymous namespace for path-printing code.  */
40 
41 namespace {
42 
43 /* Subclass of range_label for showing a particular event
44    when showing a consecutive run of events within a diagnostic_path as
45    labelled ranges within one gcc_rich_location.  */
46 
47 class path_label : public range_label
48 {
49  public:
path_label(const diagnostic_path * path,unsigned start_idx)50   path_label (const diagnostic_path *path, unsigned start_idx)
51   : m_path (path), m_start_idx (start_idx)
52   {}
53 
get_text(unsigned range_idx) const54   label_text get_text (unsigned range_idx) const FINAL OVERRIDE
55   {
56     unsigned event_idx = m_start_idx + range_idx;
57     const diagnostic_event &event = m_path->get_event (event_idx);
58 
59     /* Get the description of the event, perhaps with colorization:
60        normally, we don't colorize within a range_label, but this
61        is special-cased for diagnostic paths.  */
62     bool colorize = pp_show_color (global_dc->printer);
63     label_text event_text (event.get_desc (colorize));
64     gcc_assert (event_text.m_buffer);
65     pretty_printer pp;
66     pp_show_color (&pp) = pp_show_color (global_dc->printer);
67     diagnostic_event_id_t event_id (event_idx);
68     pp_printf (&pp, "%@ %s", &event_id, event_text.m_buffer);
69     event_text.maybe_free ();
70     label_text result = label_text::take (xstrdup (pp_formatted_text (&pp)));
71     return result;
72   }
73 
74  private:
75   const diagnostic_path *m_path;
76   unsigned m_start_idx;
77 };
78 
79 /* Return true if E1 and E2 can be consolidated into the same run of events
80    when printing a diagnostic_path.  */
81 
82 static bool
can_consolidate_events(const diagnostic_event & e1,const diagnostic_event & e2,bool check_locations)83 can_consolidate_events (const diagnostic_event &e1,
84                               const diagnostic_event &e2,
85                               bool check_locations)
86 {
87   if (e1.get_fndecl () != e2.get_fndecl ())
88     return false;
89 
90   if (e1.get_stack_depth () != e2.get_stack_depth ())
91     return false;
92 
93   if (check_locations)
94     {
95       location_t loc1 = e1.get_location ();
96       location_t loc2 = e2.get_location ();
97 
98       if (loc1 < RESERVED_LOCATION_COUNT
99             || loc2 < RESERVED_LOCATION_COUNT)
100           return false;
101 
102       /* Neither can be macro-based.  */
103       if (linemap_location_from_macro_expansion_p (line_table, loc1))
104           return false;
105       if (linemap_location_from_macro_expansion_p (line_table, loc2))
106           return false;
107     }
108 
109   /* Passed all the tests.  */
110   return true;
111 }
112 
113 /* A range of consecutive events within a diagnostic_path,
114    all with the same fndecl and stack_depth, and which are suitable
115    to print with a single call to diagnostic_show_locus.  */
116 struct event_range
117 {
event_range__anon03207f9f0111::event_range118   event_range (const diagnostic_path *path, unsigned start_idx,
119                  const diagnostic_event &initial_event)
120   : m_path (path),
121     m_initial_event (initial_event),
122     m_fndecl (initial_event.get_fndecl ()),
123     m_stack_depth (initial_event.get_stack_depth ()),
124     m_start_idx (start_idx), m_end_idx (start_idx),
125     m_path_label (path, start_idx),
126     m_richloc (initial_event.get_location (), &m_path_label)
127   {}
128 
maybe_add_event__anon03207f9f0111::event_range129   bool maybe_add_event (const diagnostic_event &new_ev, unsigned idx,
130                               bool check_rich_locations)
131   {
132     if (!can_consolidate_events (m_initial_event, new_ev,
133                                          check_rich_locations))
134       return false;
135     if (check_rich_locations)
136       if (!m_richloc.add_location_if_nearby (new_ev.get_location (),
137                                                        false, &m_path_label))
138           return false;
139     m_end_idx = idx;
140     return true;
141   }
142 
143   /* Print the events in this range to DC, typically as a single
144      call to the printer's diagnostic_show_locus.  */
145 
print__anon03207f9f0111::event_range146   void print (diagnostic_context *dc)
147   {
148     location_t initial_loc = m_initial_event.get_location ();
149 
150     /* Emit a span indicating the filename (and line/column) if the
151        line has changed relative to the last call to
152        diagnostic_show_locus.  */
153     if (dc->show_caret)
154       {
155           expanded_location exploc
156             = linemap_client_expand_location_to_spelling_point
157             (initial_loc, LOCATION_ASPECT_CARET);
158           if (exploc.file != LOCATION_FILE (dc->last_location))
159             dc->start_span (dc, exploc);
160       }
161 
162     /* If we have an UNKNOWN_LOCATION (or BUILTINS_LOCATION) as the
163        primary location for an event, diagnostic_show_locus won't print
164        anything.
165 
166        In particular the label for the event won't get printed.
167        Fail more gracefully in this case by showing the event
168        index and text, at no particular location.  */
169     if (get_pure_location (initial_loc) <= BUILTINS_LOCATION)
170       {
171           for (unsigned i = m_start_idx; i <= m_end_idx; i++)
172             {
173               const diagnostic_event &iter_event = m_path->get_event (i);
174               diagnostic_event_id_t event_id (i);
175               label_text event_text (iter_event.get_desc (true));
176               pretty_printer *pp = dc->printer;
177               pp_printf (pp, " %@: %s", &event_id, event_text.m_buffer);
178               pp_newline (pp);
179               event_text.maybe_free ();
180             }
181           return;
182       }
183 
184     /* Call diagnostic_show_locus to show the events using labels.  */
185     diagnostic_show_locus (dc, &m_richloc, DK_DIAGNOSTIC_PATH);
186 
187     /* If we have a macro expansion, show the expansion to the user.  */
188     if (linemap_location_from_macro_expansion_p (line_table, initial_loc))
189       {
190           gcc_assert (m_start_idx == m_end_idx);
191           maybe_unwind_expanded_macro_loc (dc, initial_loc);
192       }
193   }
194 
195   const diagnostic_path *m_path;
196   const diagnostic_event &m_initial_event;
197   tree m_fndecl;
198   int m_stack_depth;
199   unsigned m_start_idx;
200   unsigned m_end_idx;
201   path_label m_path_label;
202   gcc_rich_location m_richloc;
203 };
204 
205 /* A struct for grouping together the events in a diagnostic_path into
206    ranges of events, partitioned by stack frame (i.e. by fndecl and
207    stack depth).  */
208 
209 struct path_summary
210 {
211   path_summary (const diagnostic_path &path, bool check_rich_locations);
212 
get_num_ranges__anon03207f9f0111::path_summary213   unsigned get_num_ranges () const { return m_ranges.length (); }
214 
215   auto_delete_vec <event_range> m_ranges;
216 };
217 
218 /* path_summary's ctor.  */
219 
path_summary(const diagnostic_path & path,bool check_rich_locations)220 path_summary::path_summary (const diagnostic_path &path,
221                                   bool check_rich_locations)
222 {
223   const unsigned num_events = path.num_events ();
224 
225   event_range *cur_event_range = NULL;
226   for (unsigned idx = 0; idx < num_events; idx++)
227     {
228       const diagnostic_event &event = path.get_event (idx);
229       if (cur_event_range)
230           if (cur_event_range->maybe_add_event (event, idx, check_rich_locations))
231             continue;
232 
233       cur_event_range = new event_range (&path, idx, event);
234       m_ranges.safe_push (cur_event_range);
235     }
236 }
237 
238 /* Write SPACES to PP.  */
239 
240 static void
write_indent(pretty_printer * pp,int spaces)241 write_indent (pretty_printer *pp, int spaces)
242 {
243   for (int i = 0; i < spaces; i++)
244     pp_space (pp);
245 }
246 
247 /* Print FNDDECL to PP, quoting it if QUOTED is true.
248 
249    We can't use "%qE" here since we can't guarantee the capabilities
250    of PP.  */
251 
252 static void
print_fndecl(pretty_printer * pp,tree fndecl,bool quoted)253 print_fndecl (pretty_printer *pp, tree fndecl, bool quoted)
254 {
255   const char *n = DECL_NAME (fndecl)
256     ? identifier_to_locale (lang_hooks.decl_printable_name (fndecl, 2))
257     : _("<anonymous>");
258   if (quoted)
259     pp_printf (pp, "%qs", n);
260   else
261     pp_string (pp, n);
262 }
263 
264 /* Print path_summary PS to DC, giving an overview of the interprocedural
265    calls and returns.
266 
267    Print the event descriptions in a nested form, printing the event
268    descriptions within calls to diagnostic_show_locus, using labels to
269    show the events:
270 
271    'foo' (events 1-2)
272      | NN |
273      |    |
274      +--> 'bar' (events 3-4)
275             | NN |
276             |    |
277             +--> 'baz' (events 5-6)
278                    | NN |
279                    |    |
280      <------------ +
281      |
282    'foo' (events 7-8)
283      | NN |
284      |    |
285      +--> 'bar' (events 9-10)
286             | NN |
287             |    |
288             +--> 'baz' (events 11-12)
289                    | NN |
290                    |    |
291 
292    If SHOW_DEPTHS is true, append " (depth N)" to the header of each run
293    of events.
294 
295    For events with UNKNOWN_LOCATION, print a summary of each the event.  */
296 
297 void
print_path_summary_as_text(const path_summary * ps,diagnostic_context * dc,bool show_depths)298 print_path_summary_as_text (const path_summary *ps, diagnostic_context *dc,
299                                   bool show_depths)
300 {
301   pretty_printer *pp = dc->printer;
302 
303   const int per_frame_indent = 2;
304 
305   const char *const line_color = "path";
306   const char *start_line_color
307     = colorize_start (pp_show_color (pp), line_color);
308   const char *end_line_color = colorize_stop (pp_show_color (pp));
309 
310   /* Keep track of column numbers of existing '|' characters for
311      stack depths we've already printed.  */
312   const int EMPTY = -1;
313   const int DELETED = -2;
314   typedef int_hash <int, EMPTY, DELETED> vbar_hash;
315   hash_map <vbar_hash, int> vbar_column_for_depth;
316 
317   /* Print the ranges.  */
318   const int base_indent = 2;
319   int cur_indent = base_indent;
320   unsigned i;
321   event_range *range;
322   FOR_EACH_VEC_ELT (ps->m_ranges, i, range)
323     {
324       write_indent (pp, cur_indent);
325       if (i > 0)
326           {
327             const event_range *prev_range = ps->m_ranges[i - 1];
328             if (range->m_stack_depth > prev_range->m_stack_depth)
329               {
330                 /* Show pushed stack frame(s).  */
331                 const char *push_prefix = "+--> ";
332                 pp_string (pp, start_line_color);
333                 pp_string (pp, push_prefix);
334                 pp_string (pp, end_line_color);
335                 cur_indent += strlen (push_prefix);
336               }
337           }
338       if (range->m_fndecl)
339           {
340             print_fndecl (pp, range->m_fndecl, true);
341             pp_string (pp, ": ");
342           }
343       if (range->m_start_idx == range->m_end_idx)
344           pp_printf (pp, "event %i",
345                        range->m_start_idx + 1);
346       else
347           pp_printf (pp, "events %i-%i",
348                        range->m_start_idx + 1, range->m_end_idx + 1);
349       if (show_depths)
350           pp_printf (pp, " (depth %i)", range->m_stack_depth);
351       pp_newline (pp);
352 
353       /* Print a run of events.  */
354       {
355           write_indent (pp, cur_indent + per_frame_indent);
356           pp_string (pp, start_line_color);
357           pp_string (pp, "|");
358           pp_string (pp, end_line_color);
359           pp_newline (pp);
360 
361           char *saved_prefix = pp_take_prefix (pp);
362           char *prefix;
363           {
364             pretty_printer tmp_pp;
365             write_indent (&tmp_pp, cur_indent + per_frame_indent);
366             pp_string (&tmp_pp, start_line_color);
367             pp_string (&tmp_pp, "|");
368             pp_string (&tmp_pp, end_line_color);
369             prefix = xstrdup (pp_formatted_text (&tmp_pp));
370           }
371           pp_set_prefix (pp, prefix);
372           pp_prefixing_rule (pp) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
373           range->print (dc);
374           pp_set_prefix (pp, saved_prefix);
375 
376           write_indent (pp, cur_indent + per_frame_indent);
377           pp_string (pp, start_line_color);
378           pp_string (pp, "|");
379           pp_string (pp, end_line_color);
380           pp_newline (pp);
381       }
382 
383       if (i < ps->m_ranges.length () - 1)
384           {
385             const event_range *next_range = ps->m_ranges[i + 1];
386 
387             if (range->m_stack_depth > next_range->m_stack_depth)
388               {
389                 if (vbar_column_for_depth.get (next_range->m_stack_depth))
390                     {
391                       /* Show returning from stack frame(s), by printing
392                          something like:
393                          "                   |\n"
394                          "     <------------ +\n"
395                          "     |\n".  */
396                       int vbar_for_next_frame
397                         = *vbar_column_for_depth.get (next_range->m_stack_depth);
398 
399                       int indent_for_next_frame
400                         = vbar_for_next_frame - per_frame_indent;
401                       write_indent (pp, vbar_for_next_frame);
402                       pp_string (pp, start_line_color);
403                       pp_character (pp, '<');
404                       for (int i = indent_for_next_frame + per_frame_indent;
405                            i < cur_indent + per_frame_indent - 1; i++)
406                         pp_character (pp, '-');
407                       pp_character (pp, '+');
408                       pp_string (pp, end_line_color);
409                       pp_newline (pp);
410                       cur_indent = indent_for_next_frame;
411 
412                       write_indent (pp, vbar_for_next_frame);
413                       pp_string (pp, start_line_color);
414                       pp_character (pp, '|');
415                       pp_string (pp, end_line_color);
416                       pp_newline (pp);
417                     }
418                 else
419                     {
420                       /* Handle disjoint paths (e.g. a callback at some later
421                          time).  */
422                       cur_indent = base_indent;
423                     }
424               }
425             else if (range->m_stack_depth < next_range->m_stack_depth)
426               {
427                 /* Prepare to show pushed stack frame.  */
428                 gcc_assert (range->m_stack_depth != EMPTY);
429                 gcc_assert (range->m_stack_depth != DELETED);
430                 vbar_column_for_depth.put (range->m_stack_depth,
431                                                    cur_indent + per_frame_indent);
432                 cur_indent += per_frame_indent;
433               }
434 
435           }
436     }
437 }
438 
439 } /* end of anonymous namespace for path-printing code.  */
440 
441 /* Print PATH to CONTEXT, according to CONTEXT's path_format.  */
442 
443 void
default_tree_diagnostic_path_printer(diagnostic_context * context,const diagnostic_path * path)444 default_tree_diagnostic_path_printer (diagnostic_context *context,
445                                               const diagnostic_path *path)
446 {
447   gcc_assert (path);
448 
449   const unsigned num_events = path->num_events ();
450 
451   switch (context->path_format)
452     {
453     case DPF_NONE:
454       /* Do nothing.  */
455       return;
456 
457     case DPF_SEPARATE_EVENTS:
458       {
459           /* A note per event.  */
460           for (unsigned i = 0; i < num_events; i++)
461             {
462               const diagnostic_event &event = path->get_event (i);
463               label_text event_text (event.get_desc (false));
464               gcc_assert (event_text.m_buffer);
465               diagnostic_event_id_t event_id (i);
466               inform (event.get_location (),
467                         "%@ %s", &event_id, event_text.m_buffer);
468               event_text.maybe_free ();
469             }
470       }
471       break;
472 
473     case DPF_INLINE_EVENTS:
474       {
475           /* Consolidate related events.  */
476           path_summary summary (*path, true);
477           char *saved_prefix = pp_take_prefix (context->printer);
478           pp_set_prefix (context->printer, NULL);
479           print_path_summary_as_text (&summary, context,
480                                             context->show_path_depths);
481           pp_flush (context->printer);
482           pp_set_prefix (context->printer, saved_prefix);
483       }
484     }
485 }
486 
487 /* This has to be here, rather than diagnostic-format-json.cc,
488    since diagnostic-format-json.o is within OBJS-libcommon and thus
489    doesn't have access to trees (for m_fndecl).  */
490 
491 json::value *
default_tree_make_json_for_path(diagnostic_context * context,const diagnostic_path * path)492 default_tree_make_json_for_path (diagnostic_context *context,
493                                          const diagnostic_path *path)
494 {
495   json::array *path_array = new json::array ();
496   for (unsigned i = 0; i < path->num_events (); i++)
497     {
498       const diagnostic_event &event = path->get_event (i);
499 
500       json::object *event_obj = new json::object ();
501       if (event.get_location ())
502           event_obj->set ("location",
503                               json_from_expanded_location (context,
504                                                                  event.get_location ()));
505       label_text event_text (event.get_desc (false));
506       event_obj->set ("description", new json::string (event_text.m_buffer));
507       event_text.maybe_free ();
508       if (tree fndecl = event.get_fndecl ())
509           {
510             const char *function
511               = identifier_to_locale (lang_hooks.decl_printable_name (fndecl, 2));
512             event_obj->set ("function", new json::string (function));
513           }
514       event_obj->set ("depth",
515                           new json::integer_number (event.get_stack_depth ()));
516       path_array->append (event_obj);
517     }
518   return path_array;
519 }
520 
521 #if CHECKING_P
522 
523 /* Disable warnings about missing quoting in GCC diagnostics for the print
524    calls in the tests below.  */
525 #if __GNUC__ >= 10
526 #  pragma GCC diagnostic push
527 #  pragma GCC diagnostic ignored "-Wformat-diag"
528 #endif
529 
530 namespace selftest {
531 
532 /* A subclass of simple_diagnostic_path that adds member functions
533    for adding test events.  */
534 
535 class test_diagnostic_path : public simple_diagnostic_path
536 {
537  public:
test_diagnostic_path(pretty_printer * event_pp)538   test_diagnostic_path (pretty_printer *event_pp)
539   : simple_diagnostic_path (event_pp)
540   {
541   }
542 
add_entry(tree fndecl,int stack_depth)543   void add_entry (tree fndecl, int stack_depth)
544   {
545     add_event (UNKNOWN_LOCATION, fndecl, stack_depth,
546                  "entering %qE", fndecl);
547   }
548 
add_return(tree fndecl,int stack_depth)549   void add_return (tree fndecl, int stack_depth)
550   {
551     add_event (UNKNOWN_LOCATION, fndecl, stack_depth,
552                  "returning to %qE", fndecl);
553   }
554 
add_call(tree caller,int caller_stack_depth,tree callee)555   void add_call (tree caller, int caller_stack_depth, tree callee)
556   {
557     add_event (UNKNOWN_LOCATION, caller, caller_stack_depth,
558                  "calling %qE", callee);
559     add_entry (callee, caller_stack_depth + 1);
560   }
561 };
562 
563 /* Verify that empty paths are handled gracefully.  */
564 
565 static void
test_empty_path(pretty_printer * event_pp)566 test_empty_path (pretty_printer *event_pp)
567 {
568   test_diagnostic_path path (event_pp);
569   ASSERT_FALSE (path.interprocedural_p ());
570 
571   path_summary summary (path, false);
572   ASSERT_EQ (summary.get_num_ranges (), 0);
573 
574   test_diagnostic_context dc;
575   print_path_summary_as_text (&summary, &dc, true);
576   ASSERT_STREQ ("",
577                     pp_formatted_text (dc.printer));
578 }
579 
580 /* Verify that print_path_summary works on a purely intraprocedural path.  */
581 
582 static void
test_intraprocedural_path(pretty_printer * event_pp)583 test_intraprocedural_path (pretty_printer *event_pp)
584 {
585   tree fntype_void_void
586     = build_function_type_array (void_type_node, 0, NULL);
587   tree fndecl_foo = build_fn_decl ("foo", fntype_void_void);
588 
589   test_diagnostic_path path (event_pp);
590   path.add_event (UNKNOWN_LOCATION, fndecl_foo, 0, "first %qs", "free");
591   path.add_event (UNKNOWN_LOCATION, fndecl_foo, 0, "double %qs", "free");
592 
593   ASSERT_FALSE (path.interprocedural_p ());
594 
595   path_summary summary (path, false);
596   ASSERT_EQ (summary.get_num_ranges (), 1);
597 
598   test_diagnostic_context dc;
599   print_path_summary_as_text (&summary, &dc, true);
600   ASSERT_STREQ ("  `foo': events 1-2 (depth 0)\n"
601                     "    |\n"
602                     "    | (1): first `free'\n"
603                     "    | (2): double `free'\n"
604                     "    |\n",
605                     pp_formatted_text (dc.printer));
606 }
607 
608 /* Verify that print_path_summary works on an interprocedural path.  */
609 
610 static void
test_interprocedural_path_1(pretty_printer * event_pp)611 test_interprocedural_path_1 (pretty_printer *event_pp)
612 {
613   /* Build fndecls.  The types aren't quite right, but that
614      doesn't matter for the purposes of this test.  */
615   tree fntype_void_void
616     = build_function_type_array (void_type_node, 0, NULL);
617   tree fndecl_test = build_fn_decl ("test", fntype_void_void);
618   tree fndecl_make_boxed_int
619     = build_fn_decl ("make_boxed_int", fntype_void_void);
620   tree fndecl_wrapped_malloc
621     = build_fn_decl ("wrapped_malloc", fntype_void_void);
622   tree fndecl_free_boxed_int
623     = build_fn_decl ("free_boxed_int", fntype_void_void);
624   tree fndecl_wrapped_free
625     = build_fn_decl ("wrapped_free", fntype_void_void);
626 
627   test_diagnostic_path path (event_pp);
628   path.add_entry (fndecl_test, 0);
629   path.add_call (fndecl_test, 0, fndecl_make_boxed_int);
630   path.add_call (fndecl_make_boxed_int, 1, fndecl_wrapped_malloc);
631   path.add_event (UNKNOWN_LOCATION, fndecl_wrapped_malloc, 2, "calling malloc");
632   path.add_return (fndecl_test, 0);
633   path.add_call (fndecl_test, 0, fndecl_free_boxed_int);
634   path.add_call (fndecl_free_boxed_int, 1, fndecl_wrapped_free);
635   path.add_event (UNKNOWN_LOCATION, fndecl_wrapped_free, 2, "calling free");
636   path.add_return (fndecl_test, 0);
637   path.add_call (fndecl_test, 0, fndecl_free_boxed_int);
638   path.add_call (fndecl_free_boxed_int, 1, fndecl_wrapped_free);
639   path.add_event (UNKNOWN_LOCATION, fndecl_wrapped_free, 2, "calling free");
640   ASSERT_EQ (path.num_events (), 18);
641 
642   ASSERT_TRUE (path.interprocedural_p ());
643 
644   path_summary summary (path, false);
645   ASSERT_EQ (summary.get_num_ranges (), 9);
646 
647   test_diagnostic_context dc;
648   print_path_summary_as_text (&summary, &dc, true);
649   ASSERT_STREQ
650     ("  `test': events 1-2 (depth 0)\n"
651      "    |\n"
652      "    | (1): entering `test'\n"
653      "    | (2): calling `make_boxed_int'\n"
654      "    |\n"
655      "    +--> `make_boxed_int': events 3-4 (depth 1)\n"
656      "           |\n"
657      "           | (3): entering `make_boxed_int'\n"
658      "           | (4): calling `wrapped_malloc'\n"
659      "           |\n"
660      "           +--> `wrapped_malloc': events 5-6 (depth 2)\n"
661      "                  |\n"
662      "                  | (5): entering `wrapped_malloc'\n"
663      "                  | (6): calling malloc\n"
664      "                  |\n"
665      "    <-------------+\n"
666      "    |\n"
667      "  `test': events 7-8 (depth 0)\n"
668      "    |\n"
669      "    | (7): returning to `test'\n"
670      "    | (8): calling `free_boxed_int'\n"
671      "    |\n"
672      "    +--> `free_boxed_int': events 9-10 (depth 1)\n"
673      "           |\n"
674      "           | (9): entering `free_boxed_int'\n"
675      "           | (10): calling `wrapped_free'\n"
676      "           |\n"
677      "           +--> `wrapped_free': events 11-12 (depth 2)\n"
678      "                  |\n"
679      "                  | (11): entering `wrapped_free'\n"
680      "                  | (12): calling free\n"
681      "                  |\n"
682      "    <-------------+\n"
683      "    |\n"
684      "  `test': events 13-14 (depth 0)\n"
685      "    |\n"
686      "    | (13): returning to `test'\n"
687      "    | (14): calling `free_boxed_int'\n"
688      "    |\n"
689      "    +--> `free_boxed_int': events 15-16 (depth 1)\n"
690      "           |\n"
691      "           | (15): entering `free_boxed_int'\n"
692      "           | (16): calling `wrapped_free'\n"
693      "           |\n"
694      "           +--> `wrapped_free': events 17-18 (depth 2)\n"
695      "                  |\n"
696      "                  | (17): entering `wrapped_free'\n"
697      "                  | (18): calling free\n"
698      "                  |\n",
699      pp_formatted_text (dc.printer));
700 }
701 
702 /* Example where we pop the stack to an intermediate frame, rather than the
703    initial one.  */
704 
705 static void
test_interprocedural_path_2(pretty_printer * event_pp)706 test_interprocedural_path_2 (pretty_printer *event_pp)
707 {
708   /* Build fndecls.  The types aren't quite right, but that
709      doesn't matter for the purposes of this test.  */
710   tree fntype_void_void
711     = build_function_type_array (void_type_node, 0, NULL);
712   tree fndecl_foo = build_fn_decl ("foo", fntype_void_void);
713   tree fndecl_bar = build_fn_decl ("bar", fntype_void_void);
714   tree fndecl_baz = build_fn_decl ("baz", fntype_void_void);
715 
716   test_diagnostic_path path (event_pp);
717   path.add_entry (fndecl_foo, 0);
718   path.add_call (fndecl_foo, 0, fndecl_bar);
719   path.add_call (fndecl_bar, 1, fndecl_baz);
720   path.add_return (fndecl_bar, 1);
721   path.add_call (fndecl_bar, 1, fndecl_baz);
722   ASSERT_EQ (path.num_events (), 8);
723 
724   ASSERT_TRUE (path.interprocedural_p ());
725 
726   path_summary summary (path, false);
727   ASSERT_EQ (summary.get_num_ranges (), 5);
728 
729   test_diagnostic_context dc;
730   print_path_summary_as_text (&summary, &dc, true);
731   ASSERT_STREQ
732     ("  `foo': events 1-2 (depth 0)\n"
733      "    |\n"
734      "    | (1): entering `foo'\n"
735      "    | (2): calling `bar'\n"
736      "    |\n"
737      "    +--> `bar': events 3-4 (depth 1)\n"
738      "           |\n"
739      "           | (3): entering `bar'\n"
740      "           | (4): calling `baz'\n"
741      "           |\n"
742      "           +--> `baz': event 5 (depth 2)\n"
743      "                  |\n"
744      "                  | (5): entering `baz'\n"
745      "                  |\n"
746      "           <------+\n"
747      "           |\n"
748      "         `bar': events 6-7 (depth 1)\n"
749      "           |\n"
750      "           | (6): returning to `bar'\n"
751      "           | (7): calling `baz'\n"
752      "           |\n"
753      "           +--> `baz': event 8 (depth 2)\n"
754      "                  |\n"
755      "                  | (8): entering `baz'\n"
756      "                  |\n",
757      pp_formatted_text (dc.printer));
758 }
759 
760 /* Verify that print_path_summary is sane in the face of a recursive
761    diagnostic_path.  */
762 
763 static void
test_recursion(pretty_printer * event_pp)764 test_recursion (pretty_printer *event_pp)
765 {
766   tree fntype_void_void
767     = build_function_type_array (void_type_node, 0, NULL);
768   tree fndecl_factorial = build_fn_decl ("factorial", fntype_void_void);
769 
770  test_diagnostic_path path (event_pp);
771   path.add_entry (fndecl_factorial, 0);
772   for (int depth = 0; depth < 3; depth++)
773     path.add_call (fndecl_factorial, depth, fndecl_factorial);
774   ASSERT_EQ (path.num_events (), 7);
775 
776   ASSERT_TRUE (path.interprocedural_p ());
777 
778   path_summary summary (path, false);
779   ASSERT_EQ (summary.get_num_ranges (), 4);
780 
781   test_diagnostic_context dc;
782   print_path_summary_as_text (&summary, &dc, true);
783   ASSERT_STREQ
784     ("  `factorial': events 1-2 (depth 0)\n"
785      "    |\n"
786      "    | (1): entering `factorial'\n"
787      "    | (2): calling `factorial'\n"
788      "    |\n"
789      "    +--> `factorial': events 3-4 (depth 1)\n"
790      "           |\n"
791      "           | (3): entering `factorial'\n"
792      "           | (4): calling `factorial'\n"
793      "           |\n"
794      "           +--> `factorial': events 5-6 (depth 2)\n"
795      "                  |\n"
796      "                  | (5): entering `factorial'\n"
797      "                  | (6): calling `factorial'\n"
798      "                  |\n"
799      "                  +--> `factorial': event 7 (depth 3)\n"
800      "                         |\n"
801      "                         | (7): entering `factorial'\n"
802      "                         |\n",
803      pp_formatted_text (dc.printer));
804 }
805 
806 /* Run all of the selftests within this file.  */
807 
808 void
tree_diagnostic_path_cc_tests()809 tree_diagnostic_path_cc_tests ()
810 {
811   auto_fix_quotes fix_quotes;
812   pretty_printer *event_pp = global_dc->printer->clone ();
813   pp_show_color (event_pp) = 0;
814   test_empty_path (event_pp);
815   test_intraprocedural_path (event_pp);
816   test_interprocedural_path_1 (event_pp);
817   test_interprocedural_path_2 (event_pp);
818   test_recursion (event_pp);
819   delete event_pp;
820 }
821 
822 } // namespace selftest
823 
824 #if __GNUC__ >= 10
825 #  pragma GCC diagnostic pop
826 #endif
827 
828 #endif /* #if CHECKING_P */
829