| /* Paths through the code associated with a diagnostic. |
| Copyright (C) 2019-2022 Free Software Foundation, Inc. |
| Contributed by David Malcolm <dmalcolm@redhat.com> |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it under |
| the terms of the GNU General Public License as published by the Free |
| Software Foundation; either version 3, or (at your option) any later |
| version. |
| |
| GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "tree.h" |
| #include "diagnostic.h" |
| #include "tree-pretty-print.h" |
| #include "gimple-pretty-print.h" |
| #include "tree-diagnostic.h" |
| #include "langhooks.h" |
| #include "intl.h" |
| #include "diagnostic-path.h" |
| #include "json.h" |
| #include "gcc-rich-location.h" |
| #include "diagnostic-color.h" |
| #include "diagnostic-event-id.h" |
| #include "selftest.h" |
| #include "selftest-diagnostic.h" |
| |
| /* Anonymous namespace for path-printing code. */ |
| |
| namespace { |
| |
| /* Subclass of range_label for showing a particular event |
| when showing a consecutive run of events within a diagnostic_path as |
| labelled ranges within one gcc_rich_location. */ |
| |
| class path_label : public range_label |
| { |
| public: |
| path_label (const diagnostic_path *path, unsigned start_idx) |
| : m_path (path), m_start_idx (start_idx) |
| {} |
| |
| label_text get_text (unsigned range_idx) const FINAL OVERRIDE |
| { |
| unsigned event_idx = m_start_idx + range_idx; |
| const diagnostic_event &event = m_path->get_event (event_idx); |
| |
| /* Get the description of the event, perhaps with colorization: |
| normally, we don't colorize within a range_label, but this |
| is special-cased for diagnostic paths. */ |
| bool colorize = pp_show_color (global_dc->printer); |
| label_text event_text (event.get_desc (colorize)); |
| gcc_assert (event_text.m_buffer); |
| pretty_printer pp; |
| pp_show_color (&pp) = pp_show_color (global_dc->printer); |
| diagnostic_event_id_t event_id (event_idx); |
| pp_printf (&pp, "%@ %s", &event_id, event_text.m_buffer); |
| event_text.maybe_free (); |
| label_text result = label_text::take (xstrdup (pp_formatted_text (&pp))); |
| return result; |
| } |
| |
| private: |
| const diagnostic_path *m_path; |
| unsigned m_start_idx; |
| }; |
| |
| /* Return true if E1 and E2 can be consolidated into the same run of events |
| when printing a diagnostic_path. */ |
| |
| static bool |
| can_consolidate_events (const diagnostic_event &e1, |
| const diagnostic_event &e2, |
| bool check_locations) |
| { |
| if (e1.get_fndecl () != e2.get_fndecl ()) |
| return false; |
| |
| if (e1.get_stack_depth () != e2.get_stack_depth ()) |
| return false; |
| |
| if (check_locations) |
| { |
| location_t loc1 = e1.get_location (); |
| location_t loc2 = e2.get_location (); |
| |
| if (loc1 < RESERVED_LOCATION_COUNT |
| || loc2 < RESERVED_LOCATION_COUNT) |
| return false; |
| |
| /* Neither can be macro-based. */ |
| if (linemap_location_from_macro_expansion_p (line_table, loc1)) |
| return false; |
| if (linemap_location_from_macro_expansion_p (line_table, loc2)) |
| return false; |
| } |
| |
| /* Passed all the tests. */ |
| return true; |
| } |
| |
| /* A range of consecutive events within a diagnostic_path, |
| all with the same fndecl and stack_depth, and which are suitable |
| to print with a single call to diagnostic_show_locus. */ |
| struct event_range |
| { |
| event_range (const diagnostic_path *path, unsigned start_idx, |
| const diagnostic_event &initial_event) |
| : m_path (path), |
| m_initial_event (initial_event), |
| m_fndecl (initial_event.get_fndecl ()), |
| m_stack_depth (initial_event.get_stack_depth ()), |
| m_start_idx (start_idx), m_end_idx (start_idx), |
| m_path_label (path, start_idx), |
| m_richloc (initial_event.get_location (), &m_path_label) |
| {} |
| |
| bool maybe_add_event (const diagnostic_event &new_ev, unsigned idx, |
| bool check_rich_locations) |
| { |
| if (!can_consolidate_events (m_initial_event, new_ev, |
| check_rich_locations)) |
| return false; |
| if (check_rich_locations) |
| if (!m_richloc.add_location_if_nearby (new_ev.get_location (), |
| false, &m_path_label)) |
| return false; |
| m_end_idx = idx; |
| return true; |
| } |
| |
| /* Print the events in this range to DC, typically as a single |
| call to the printer's diagnostic_show_locus. */ |
| |
| void print (diagnostic_context *dc) |
| { |
| location_t initial_loc = m_initial_event.get_location (); |
| |
| /* Emit a span indicating the filename (and line/column) if the |
| line has changed relative to the last call to |
| diagnostic_show_locus. */ |
| if (dc->show_caret) |
| { |
| expanded_location exploc |
| = linemap_client_expand_location_to_spelling_point |
| (initial_loc, LOCATION_ASPECT_CARET); |
| if (exploc.file != LOCATION_FILE (dc->last_location)) |
| dc->start_span (dc, exploc); |
| } |
| |
| /* If we have an UNKNOWN_LOCATION (or BUILTINS_LOCATION) as the |
| primary location for an event, diagnostic_show_locus won't print |
| anything. |
| |
| In particular the label for the event won't get printed. |
| Fail more gracefully in this case by showing the event |
| index and text, at no particular location. */ |
| if (get_pure_location (initial_loc) <= BUILTINS_LOCATION) |
| { |
| for (unsigned i = m_start_idx; i <= m_end_idx; i++) |
| { |
| const diagnostic_event &iter_event = m_path->get_event (i); |
| diagnostic_event_id_t event_id (i); |
| label_text event_text (iter_event.get_desc (true)); |
| pretty_printer *pp = dc->printer; |
| pp_printf (pp, " %@: %s", &event_id, event_text.m_buffer); |
| pp_newline (pp); |
| event_text.maybe_free (); |
| } |
| return; |
| } |
| |
| /* Call diagnostic_show_locus to show the events using labels. */ |
| diagnostic_show_locus (dc, &m_richloc, DK_DIAGNOSTIC_PATH); |
| |
| /* If we have a macro expansion, show the expansion to the user. */ |
| if (linemap_location_from_macro_expansion_p (line_table, initial_loc)) |
| { |
| gcc_assert (m_start_idx == m_end_idx); |
| maybe_unwind_expanded_macro_loc (dc, initial_loc); |
| } |
| } |
| |
| const diagnostic_path *m_path; |
| const diagnostic_event &m_initial_event; |
| tree m_fndecl; |
| int m_stack_depth; |
| unsigned m_start_idx; |
| unsigned m_end_idx; |
| path_label m_path_label; |
| gcc_rich_location m_richloc; |
| }; |
| |
| /* A struct for grouping together the events in a diagnostic_path into |
| ranges of events, partitioned by stack frame (i.e. by fndecl and |
| stack depth). */ |
| |
| struct path_summary |
| { |
| path_summary (const diagnostic_path &path, bool check_rich_locations); |
| |
| unsigned get_num_ranges () const { return m_ranges.length (); } |
| |
| auto_delete_vec <event_range> m_ranges; |
| }; |
| |
| /* path_summary's ctor. */ |
| |
| path_summary::path_summary (const diagnostic_path &path, |
| bool check_rich_locations) |
| { |
| const unsigned num_events = path.num_events (); |
| |
| event_range *cur_event_range = NULL; |
| for (unsigned idx = 0; idx < num_events; idx++) |
| { |
| const diagnostic_event &event = path.get_event (idx); |
| if (cur_event_range) |
| if (cur_event_range->maybe_add_event (event, idx, check_rich_locations)) |
| continue; |
| |
| cur_event_range = new event_range (&path, idx, event); |
| m_ranges.safe_push (cur_event_range); |
| } |
| } |
| |
| /* Write SPACES to PP. */ |
| |
| static void |
| write_indent (pretty_printer *pp, int spaces) |
| { |
| for (int i = 0; i < spaces; i++) |
| pp_space (pp); |
| } |
| |
| /* Print FNDDECL to PP, quoting it if QUOTED is true. |
| |
| We can't use "%qE" here since we can't guarantee the capabilities |
| of PP. */ |
| |
| static void |
| 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>"); |
| if (quoted) |
| pp_printf (pp, "%qs", n); |
| else |
| pp_string (pp, n); |
| } |
| |
| /* Print path_summary PS to DC, giving an overview of the interprocedural |
| calls and returns. |
| |
| Print the event descriptions in a nested form, printing the event |
| descriptions within calls to diagnostic_show_locus, using labels to |
| show the events: |
| |
| 'foo' (events 1-2) |
| | NN | |
| | | |
| +--> 'bar' (events 3-4) |
| | NN | |
| | | |
| +--> 'baz' (events 5-6) |
| | NN | |
| | | |
| <------------ + |
| | |
| 'foo' (events 7-8) |
| | NN | |
| | | |
| +--> 'bar' (events 9-10) |
| | NN | |
| | | |
| +--> 'baz' (events 11-12) |
| | NN | |
| | | |
| |
| If SHOW_DEPTHS is true, append " (depth N)" to the header of each run |
| of events. |
| |
| For events with UNKNOWN_LOCATION, print a summary of each the event. */ |
| |
| void |
| print_path_summary_as_text (const path_summary *ps, diagnostic_context *dc, |
| bool show_depths) |
| { |
| pretty_printer *pp = dc->printer; |
| |
| const int per_frame_indent = 2; |
| |
| const char *const line_color = "path"; |
| const char *start_line_color |
| = colorize_start (pp_show_color (pp), line_color); |
| const char *end_line_color = colorize_stop (pp_show_color (pp)); |
| |
| /* 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; |
| int cur_indent = base_indent; |
| unsigned i; |
| event_range *range; |
| FOR_EACH_VEC_ELT (ps->m_ranges, i, range) |
| { |
| write_indent (pp, cur_indent); |
| if (i > 0) |
| { |
| const event_range *prev_range = ps->m_ranges[i - 1]; |
| if (range->m_stack_depth > prev_range->m_stack_depth) |
| { |
| /* 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); |
| } |
| } |
| if (range->m_fndecl) |
| { |
| print_fndecl (pp, range->m_fndecl, true); |
| pp_string (pp, ": "); |
| } |
| if (range->m_start_idx == range->m_end_idx) |
| pp_printf (pp, "event %i", |
| range->m_start_idx + 1); |
| else |
| pp_printf (pp, "events %i-%i", |
| range->m_start_idx + 1, range->m_end_idx + 1); |
| if (show_depths) |
| pp_printf (pp, " (depth %i)", range->m_stack_depth); |
| pp_newline (pp); |
| |
| /* Print a run of events. */ |
| { |
| write_indent (pp, cur_indent + per_frame_indent); |
| pp_string (pp, start_line_color); |
| pp_string (pp, "|"); |
| pp_string (pp, end_line_color); |
| pp_newline (pp); |
| |
| char *saved_prefix = pp_take_prefix (pp); |
| char *prefix; |
| { |
| pretty_printer tmp_pp; |
| write_indent (&tmp_pp, cur_indent + per_frame_indent); |
| pp_string (&tmp_pp, start_line_color); |
| pp_string (&tmp_pp, "|"); |
| pp_string (&tmp_pp, end_line_color); |
| prefix = xstrdup (pp_formatted_text (&tmp_pp)); |
| } |
| pp_set_prefix (pp, prefix); |
| pp_prefixing_rule (pp) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE; |
| range->print (dc); |
| pp_set_prefix (pp, saved_prefix); |
| |
| write_indent (pp, cur_indent + per_frame_indent); |
| pp_string (pp, start_line_color); |
| pp_string (pp, "|"); |
| pp_string (pp, end_line_color); |
| pp_newline (pp); |
| } |
| |
| if (i < ps->m_ranges.length () - 1) |
| { |
| const event_range *next_range = ps->m_ranges[i + 1]; |
| |
| if (range->m_stack_depth > next_range->m_stack_depth) |
| { |
| if (vbar_column_for_depth.get (next_range->m_stack_depth)) |
| { |
| /* Show returning from stack frame(s), by printing |
| something like: |
| " |\n" |
| " <------------ +\n" |
| " |\n". */ |
| int vbar_for_next_frame |
| = *vbar_column_for_depth.get (next_range->m_stack_depth); |
| |
| int indent_for_next_frame |
| = vbar_for_next_frame - per_frame_indent; |
| write_indent (pp, vbar_for_next_frame); |
| pp_string (pp, start_line_color); |
| pp_character (pp, '<'); |
| for (int i = indent_for_next_frame + per_frame_indent; |
| i < cur_indent + per_frame_indent - 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, vbar_for_next_frame); |
| pp_string (pp, start_line_color); |
| pp_character (pp, '|'); |
| pp_string (pp, end_line_color); |
| pp_newline (pp); |
| } |
| else |
| { |
| /* Handle disjoint paths (e.g. a callback at some later |
| time). */ |
| cur_indent = base_indent; |
| } |
| } |
| else if (range->m_stack_depth < next_range->m_stack_depth) |
| { |
| /* Prepare to show pushed stack frame. */ |
| 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 + per_frame_indent); |
| cur_indent += per_frame_indent; |
| } |
| |
| } |
| } |
| } |
| |
| } /* end of anonymous namespace for path-printing code. */ |
| |
| /* Print PATH to CONTEXT, according to CONTEXT's path_format. */ |
| |
| void |
| default_tree_diagnostic_path_printer (diagnostic_context *context, |
| const diagnostic_path *path) |
| { |
| gcc_assert (path); |
| |
| const unsigned num_events = path->num_events (); |
| |
| switch (context->path_format) |
| { |
| case DPF_NONE: |
| /* Do nothing. */ |
| return; |
| |
| case DPF_SEPARATE_EVENTS: |
| { |
| /* A note per event. */ |
| for (unsigned i = 0; i < num_events; i++) |
| { |
| const diagnostic_event &event = path->get_event (i); |
| label_text event_text (event.get_desc (false)); |
| gcc_assert (event_text.m_buffer); |
| diagnostic_event_id_t event_id (i); |
| inform (event.get_location (), |
| "%@ %s", &event_id, event_text.m_buffer); |
| event_text.maybe_free (); |
| } |
| } |
| break; |
| |
| case DPF_INLINE_EVENTS: |
| { |
| /* Consolidate related events. */ |
| path_summary summary (*path, true); |
| char *saved_prefix = pp_take_prefix (context->printer); |
| pp_set_prefix (context->printer, NULL); |
| print_path_summary_as_text (&summary, context, |
| context->show_path_depths); |
| pp_flush (context->printer); |
| pp_set_prefix (context->printer, saved_prefix); |
| } |
| } |
| } |
| |
| /* This has to be here, rather than diagnostic-format-json.cc, |
| since diagnostic-format-json.o is within OBJS-libcommon and thus |
| doesn't have access to trees (for m_fndecl). */ |
| |
| json::value * |
| default_tree_make_json_for_path (diagnostic_context *context, |
| const diagnostic_path *path) |
| { |
| json::array *path_array = new json::array (); |
| for (unsigned i = 0; i < path->num_events (); i++) |
| { |
| const diagnostic_event &event = path->get_event (i); |
| |
| json::object *event_obj = new json::object (); |
| if (event.get_location ()) |
| event_obj->set ("location", |
| json_from_expanded_location (context, |
| event.get_location ())); |
| label_text event_text (event.get_desc (false)); |
| event_obj->set ("description", new json::string (event_text.m_buffer)); |
| event_text.maybe_free (); |
| if (tree fndecl = event.get_fndecl ()) |
| { |
| const char *function |
| = identifier_to_locale (lang_hooks.decl_printable_name (fndecl, 2)); |
| event_obj->set ("function", new json::string (function)); |
| } |
| event_obj->set ("depth", |
| new json::integer_number (event.get_stack_depth ())); |
| path_array->append (event_obj); |
| } |
| return path_array; |
| } |
| |
| #if CHECKING_P |
| |
| /* Disable warnings about missing quoting in GCC diagnostics for the print |
| calls in the tests below. */ |
| #if __GNUC__ >= 10 |
| # pragma GCC diagnostic push |
| # pragma GCC diagnostic ignored "-Wformat-diag" |
| #endif |
| |
| namespace selftest { |
| |
| /* A subclass of simple_diagnostic_path that adds member functions |
| for adding test events. */ |
| |
| 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, |
| "entering %qE", fndecl); |
| } |
| |
| void add_return (tree fndecl, int stack_depth) |
| { |
| add_event (UNKNOWN_LOCATION, fndecl, stack_depth, |
| "returning to %qE", fndecl); |
| } |
| |
| void add_call (tree caller, int caller_stack_depth, tree callee) |
| { |
| add_event (UNKNOWN_LOCATION, caller, caller_stack_depth, |
| "calling %qE", callee); |
| add_entry (callee, caller_stack_depth + 1); |
| } |
| }; |
| |
| /* Verify that empty paths are handled gracefully. */ |
| |
| static void |
| test_empty_path (pretty_printer *event_pp) |
| { |
| test_diagnostic_path path (event_pp); |
| ASSERT_FALSE (path.interprocedural_p ()); |
| |
| path_summary summary (path, false); |
| ASSERT_EQ (summary.get_num_ranges (), 0); |
| |
| test_diagnostic_context dc; |
| print_path_summary_as_text (&summary, &dc, true); |
| ASSERT_STREQ ("", |
| pp_formatted_text (dc.printer)); |
| } |
| |
| /* Verify that print_path_summary works on a purely intraprocedural path. */ |
| |
| static void |
| 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 ()); |
| |
| path_summary summary (path, false); |
| ASSERT_EQ (summary.get_num_ranges (), 1); |
| |
| test_diagnostic_context dc; |
| print_path_summary_as_text (&summary, &dc, true); |
| ASSERT_STREQ (" `foo': events 1-2 (depth 0)\n" |
| " |\n" |
| " | (1): first `free'\n" |
| " | (2): double `free'\n" |
| " |\n", |
| pp_formatted_text (dc.printer)); |
| } |
| |
| /* Verify that print_path_summary works on an interprocedural path. */ |
| |
| 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. */ |
| tree fntype_void_void |
| = build_function_type_array (void_type_node, 0, NULL); |
| tree fndecl_test = build_fn_decl ("test", fntype_void_void); |
| tree fndecl_make_boxed_int |
| = build_fn_decl ("make_boxed_int", fntype_void_void); |
| tree fndecl_wrapped_malloc |
| = build_fn_decl ("wrapped_malloc", fntype_void_void); |
| tree fndecl_free_boxed_int |
| = build_fn_decl ("free_boxed_int", fntype_void_void); |
| tree fndecl_wrapped_free |
| = build_fn_decl ("wrapped_free", fntype_void_void); |
| |
| 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); |
| path.add_event (UNKNOWN_LOCATION, fndecl_wrapped_malloc, 2, "calling malloc"); |
| path.add_return (fndecl_test, 0); |
| path.add_call (fndecl_test, 0, fndecl_free_boxed_int); |
| path.add_call (fndecl_free_boxed_int, 1, fndecl_wrapped_free); |
| path.add_event (UNKNOWN_LOCATION, fndecl_wrapped_free, 2, "calling free"); |
| path.add_return (fndecl_test, 0); |
| path.add_call (fndecl_test, 0, fndecl_free_boxed_int); |
| path.add_call (fndecl_free_boxed_int, 1, fndecl_wrapped_free); |
| path.add_event (UNKNOWN_LOCATION, fndecl_wrapped_free, 2, "calling free"); |
| ASSERT_EQ (path.num_events (), 18); |
| |
| ASSERT_TRUE (path.interprocedural_p ()); |
| |
| path_summary summary (path, false); |
| ASSERT_EQ (summary.get_num_ranges (), 9); |
| |
| test_diagnostic_context dc; |
| print_path_summary_as_text (&summary, &dc, true); |
| ASSERT_STREQ |
| (" `test': events 1-2 (depth 0)\n" |
| " |\n" |
| " | (1): entering `test'\n" |
| " | (2): calling `make_boxed_int'\n" |
| " |\n" |
| " +--> `make_boxed_int': events 3-4 (depth 1)\n" |
| " |\n" |
| " | (3): entering `make_boxed_int'\n" |
| " | (4): calling `wrapped_malloc'\n" |
| " |\n" |
| " +--> `wrapped_malloc': events 5-6 (depth 2)\n" |
| " |\n" |
| " | (5): entering `wrapped_malloc'\n" |
| " | (6): calling malloc\n" |
| " |\n" |
| " <-------------+\n" |
| " |\n" |
| " `test': events 7-8 (depth 0)\n" |
| " |\n" |
| " | (7): returning to `test'\n" |
| " | (8): calling `free_boxed_int'\n" |
| " |\n" |
| " +--> `free_boxed_int': events 9-10 (depth 1)\n" |
| " |\n" |
| " | (9): entering `free_boxed_int'\n" |
| " | (10): calling `wrapped_free'\n" |
| " |\n" |
| " +--> `wrapped_free': events 11-12 (depth 2)\n" |
| " |\n" |
| " | (11): entering `wrapped_free'\n" |
| " | (12): calling free\n" |
| " |\n" |
| " <-------------+\n" |
| " |\n" |
| " `test': events 13-14 (depth 0)\n" |
| " |\n" |
| " | (13): returning to `test'\n" |
| " | (14): calling `free_boxed_int'\n" |
| " |\n" |
| " +--> `free_boxed_int': events 15-16 (depth 1)\n" |
| " |\n" |
| " | (15): entering `free_boxed_int'\n" |
| " | (16): calling `wrapped_free'\n" |
| " |\n" |
| " +--> `wrapped_free': events 17-18 (depth 2)\n" |
| " |\n" |
| " | (17): entering `wrapped_free'\n" |
| " | (18): calling free\n" |
| " |\n", |
| pp_formatted_text (dc.printer)); |
| } |
| |
| /* 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 ()); |
| |
| path_summary summary (path, false); |
| ASSERT_EQ (summary.get_num_ranges (), 5); |
| |
| test_diagnostic_context dc; |
| print_path_summary_as_text (&summary, &dc, true); |
| ASSERT_STREQ |
| (" `foo': events 1-2 (depth 0)\n" |
| " |\n" |
| " | (1): entering `foo'\n" |
| " | (2): calling `bar'\n" |
| " |\n" |
| " +--> `bar': events 3-4 (depth 1)\n" |
| " |\n" |
| " | (3): entering `bar'\n" |
| " | (4): calling `baz'\n" |
| " |\n" |
| " +--> `baz': event 5 (depth 2)\n" |
| " |\n" |
| " | (5): entering `baz'\n" |
| " |\n" |
| " <------+\n" |
| " |\n" |
| " `bar': events 6-7 (depth 1)\n" |
| " |\n" |
| " | (6): returning to `bar'\n" |
| " | (7): calling `baz'\n" |
| " |\n" |
| " +--> `baz': event 8 (depth 2)\n" |
| " |\n" |
| " | (8): entering `baz'\n" |
| " |\n", |
| pp_formatted_text (dc.printer)); |
| } |
| |
| /* Verify that print_path_summary is sane in the face of a recursive |
| diagnostic_path. */ |
| |
| 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 ()); |
| |
| path_summary summary (path, false); |
| ASSERT_EQ (summary.get_num_ranges (), 4); |
| |
| test_diagnostic_context dc; |
| print_path_summary_as_text (&summary, &dc, true); |
| ASSERT_STREQ |
| (" `factorial': events 1-2 (depth 0)\n" |
| " |\n" |
| " | (1): entering `factorial'\n" |
| " | (2): calling `factorial'\n" |
| " |\n" |
| " +--> `factorial': events 3-4 (depth 1)\n" |
| " |\n" |
| " | (3): entering `factorial'\n" |
| " | (4): calling `factorial'\n" |
| " |\n" |
| " +--> `factorial': events 5-6 (depth 2)\n" |
| " |\n" |
| " | (5): entering `factorial'\n" |
| " | (6): calling `factorial'\n" |
| " |\n" |
| " +--> `factorial': event 7 (depth 3)\n" |
| " |\n" |
| " | (7): entering `factorial'\n" |
| " |\n", |
| pp_formatted_text (dc.printer)); |
| } |
| |
| /* Run all of the selftests within this file. */ |
| |
| void |
| tree_diagnostic_path_cc_tests () |
| { |
| auto_fix_quotes fix_quotes; |
| 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 |
| |
| #if __GNUC__ >= 10 |
| # pragma GCC diagnostic pop |
| #endif |
| |
| #endif /* #if CHECKING_P */ |