| /* Concrete classes for implementing diagnostic paths, using tree. |
| Copyright (C) 2019-2025 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" |
| #define INCLUDE_VECTOR |
| #include "system.h" |
| #include "coretypes.h" |
| #include "tree.h" |
| #include "version.h" |
| #include "demangle.h" |
| #include "intl.h" |
| #include "backtrace.h" |
| #include "diagnostic.h" |
| #include "simple-diagnostic-path.h" |
| #include "selftest.h" |
| |
| using namespace diagnostics::paths; |
| |
| /* class simple_diagnostic_path : public diagnostics::paths::path. */ |
| |
| simple_diagnostic_path:: |
| simple_diagnostic_path (const tree_logical_location_manager &logical_loc_mgr, |
| pretty_printer *event_pp) |
| : path (logical_loc_mgr), |
| m_event_pp (event_pp), |
| m_localize_events (true) |
| { |
| add_thread ("main"); |
| } |
| |
| /* Implementation of path::get_event vfunc for |
| simple_diagnostic_path: simply return the event in the vec. */ |
| |
| const event & |
| simple_diagnostic_path::get_event (int idx) const |
| { |
| return *m_events[idx]; |
| } |
| |
| const thread & |
| simple_diagnostic_path::get_thread (thread_id_t idx) const |
| { |
| return *m_threads[idx]; |
| } |
| |
| bool |
| simple_diagnostic_path::same_function_p (int event_idx_a, |
| int event_idx_b) const |
| { |
| return (m_events[event_idx_a]->get_fndecl () |
| == m_events[event_idx_b]->get_fndecl ()); |
| } |
| |
| thread_id_t |
| simple_diagnostic_path::add_thread (const char *name) |
| { |
| m_threads.safe_push (new simple_diagnostic_thread (name)); |
| return m_threads.length () - 1; |
| } |
| |
| /* Add an event to this path at LOC within function FNDECL at |
| stack depth DEPTH. |
| |
| Use m_context's printer to format FMT, as the text of the new |
| event. Localize FMT iff m_localize_events is set. |
| |
| Return the id of the new event. */ |
| |
| event_id_t |
| simple_diagnostic_path::add_event (location_t loc, tree fndecl, int depth, |
| const char *fmt, ...) |
| { |
| pretty_printer *pp = m_event_pp; |
| pp_clear_output_area (pp); |
| |
| rich_location rich_loc (line_table, UNKNOWN_LOCATION); |
| |
| va_list ap; |
| |
| va_start (ap, fmt); |
| |
| text_info ti (m_localize_events ? _(fmt) : fmt, |
| &ap, 0, nullptr, &rich_loc); |
| pp_format (pp, &ti); |
| pp_output_formatted_text (pp); |
| |
| va_end (ap); |
| |
| simple_diagnostic_event *new_event |
| = new simple_diagnostic_event (loc, fndecl, depth, pp_formatted_text (pp)); |
| m_events.safe_push (new_event); |
| |
| pp_clear_output_area (pp); |
| |
| return event_id_t (m_events.length () - 1); |
| } |
| |
| event_id_t |
| simple_diagnostic_path::add_thread_event (thread_id_t thread_id, |
| location_t loc, |
| tree fndecl, |
| int depth, |
| const char *fmt, ...) |
| { |
| pretty_printer *pp = m_event_pp; |
| pp_clear_output_area (pp); |
| |
| rich_location rich_loc (line_table, UNKNOWN_LOCATION); |
| |
| va_list ap; |
| |
| va_start (ap, fmt); |
| |
| text_info ti (_(fmt), &ap, 0, nullptr, &rich_loc); |
| |
| pp_format (pp, &ti); |
| pp_output_formatted_text (pp); |
| |
| va_end (ap); |
| |
| simple_diagnostic_event *new_event |
| = new simple_diagnostic_event (loc, fndecl, depth, pp_formatted_text (pp), |
| thread_id); |
| m_events.safe_push (new_event); |
| |
| pp_clear_output_area (pp); |
| |
| return event_id_t (m_events.length () - 1); |
| } |
| |
| /* Mark the most recent event on this path (which must exist) as being |
| connected to the next one to be added. */ |
| |
| void |
| simple_diagnostic_path::connect_to_next_event () |
| { |
| gcc_assert (m_events.length () > 0); |
| m_events[m_events.length () - 1]->connect_to_next_event (); |
| } |
| |
| /* struct simple_diagnostic_event. */ |
| |
| /* simple_diagnostic_event's ctor. */ |
| |
| simple_diagnostic_event:: |
| simple_diagnostic_event (location_t loc, |
| tree fndecl, |
| int depth, |
| const char *desc, |
| thread_id_t thread_id) |
| : m_loc (loc), m_fndecl (fndecl), |
| m_logical_loc (tree_logical_location_manager::key_from_tree (fndecl)), |
| m_depth (depth), m_desc (xstrdup (desc)), |
| m_connected_to_next_event (false), |
| m_thread_id (thread_id) |
| { |
| } |
| |
| /* simple_diagnostic_event's dtor. */ |
| |
| simple_diagnostic_event::~simple_diagnostic_event () |
| { |
| free (m_desc); |
| } |
| |
| void |
| simple_diagnostic_event::print_desc (pretty_printer &pp) const |
| { |
| pp_string (&pp, m_desc); |
| } |
| |
| #if CHECKING_P |
| |
| namespace selftest { |
| |
| static void |
| test_intraprocedural_path (pretty_printer *event_pp) |
| { |
| tree_logical_location_manager mgr; |
| tree fntype_void_void |
| = build_function_type_array (void_type_node, 0, nullptr); |
| tree fndecl_foo = build_fn_decl ("foo", fntype_void_void); |
| |
| simple_diagnostic_path path (mgr, 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_EQ (path.num_events (), 2); |
| ASSERT_EQ (path.num_threads (), 1); |
| ASSERT_FALSE (path.interprocedural_p ()); |
| ASSERT_STREQ (path.get_event (0).get_desc (*event_pp).get (), |
| "first `free'"); |
| ASSERT_STREQ (path.get_event (1).get_desc (*event_pp).get (), |
| "double `free'"); |
| } |
| |
| /* Run all of the selftests within this file. */ |
| |
| void |
| simple_diagnostic_path_cc_tests () |
| { |
| /* In a few places we use the global dc's printer to determine |
| colorization so ensure this off during the tests. */ |
| pretty_printer *global_pp = global_dc->get_reference_printer (); |
| const bool saved_show_color = pp_show_color (global_pp); |
| pp_show_color (global_pp) = false; |
| |
| auto_fix_quotes fix_quotes; |
| std::unique_ptr<pretty_printer> event_pp |
| = std::unique_ptr<pretty_printer> (global_pp->clone ()); |
| |
| test_intraprocedural_path (event_pp.get ()); |
| |
| pp_show_color (global_pp) = saved_show_color; |
| } |
| |
| } // namespace selftest |
| |
| #endif /* #if CHECKING_P */ |