1/* Concrete classes for implementing diagnostic paths.
2 Copyright (C) 2019-2025 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 3, or (at your option) any later
10version.
11
12GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15for more details.
16
17You should have received a copy of the GNU General Public License
18along with GCC; see the file COPYING3. If not see
19<http://www.gnu.org/licenses/>. */
20
21
22#include "config.h"
23#define INCLUDE_VECTOR
24#include "system.h"
25#include "coretypes.h"
26#include "tree.h"
27#include "version.h"
28#include "demangle.h"
29#include "intl.h"
30#include "backtrace.h"
31#include "diagnostic.h"
32#include "simple-diagnostic-path.h"
33#include "selftest.h"
34
35/* class simple_diagnostic_path : public diagnostic_path. */
36
37simple_diagnostic_path::
38simple_diagnostic_path (const tree_logical_location_manager &logical_loc_mgr,
39 pretty_printer *event_pp)
40: diagnostic_path (logical_loc_mgr),
41 m_event_pp (event_pp),
42 m_localize_events (true)
43{
44 add_thread (name: "main");
45}
46
47/* Implementation of diagnostic_path::get_event vfunc for
48 simple_diagnostic_path: simply return the event in the vec. */
49
50const diagnostic_event &
51simple_diagnostic_path::get_event (int idx) const
52{
53 return *m_events[idx];
54}
55
56const diagnostic_thread &
57simple_diagnostic_path::get_thread (diagnostic_thread_id_t idx) const
58{
59 return *m_threads[idx];
60}
61
62bool
63simple_diagnostic_path::same_function_p (int event_idx_a,
64 int event_idx_b) const
65{
66 return (m_events[event_idx_a]->get_fndecl ()
67 == m_events[event_idx_b]->get_fndecl ());
68}
69
70diagnostic_thread_id_t
71simple_diagnostic_path::add_thread (const char *name)
72{
73 m_threads.safe_push (obj: new simple_diagnostic_thread (name));
74 return m_threads.length () - 1;
75}
76
77/* Add an event to this path at LOC within function FNDECL at
78 stack depth DEPTH.
79
80 Use m_context's printer to format FMT, as the text of the new
81 event. Localize FMT iff m_localize_events is set.
82
83 Return the id of the new event. */
84
85diagnostic_event_id_t
86simple_diagnostic_path::add_event (location_t loc, tree fndecl, int depth,
87 const char *fmt, ...)
88{
89 pretty_printer *pp = m_event_pp;
90 pp_clear_output_area (pp);
91
92 rich_location rich_loc (line_table, UNKNOWN_LOCATION);
93
94 va_list ap;
95
96 va_start (ap, fmt);
97
98 text_info ti (m_localize_events ? _(fmt) : fmt,
99 &ap, 0, nullptr, &rich_loc);
100 pp_format (pp, text: &ti);
101 pp_output_formatted_text (pp);
102
103 va_end (ap);
104
105 simple_diagnostic_event *new_event
106 = new simple_diagnostic_event (loc, fndecl, depth, pp_formatted_text (pp));
107 m_events.safe_push (obj: new_event);
108
109 pp_clear_output_area (pp);
110
111 return diagnostic_event_id_t (m_events.length () - 1);
112}
113
114diagnostic_event_id_t
115simple_diagnostic_path::add_thread_event (diagnostic_thread_id_t thread_id,
116 location_t loc,
117 tree fndecl,
118 int depth,
119 const char *fmt, ...)
120{
121 pretty_printer *pp = m_event_pp;
122 pp_clear_output_area (pp);
123
124 rich_location rich_loc (line_table, UNKNOWN_LOCATION);
125
126 va_list ap;
127
128 va_start (ap, fmt);
129
130 text_info ti (_(fmt), &ap, 0, nullptr, &rich_loc);
131
132 pp_format (pp, text: &ti);
133 pp_output_formatted_text (pp);
134
135 va_end (ap);
136
137 simple_diagnostic_event *new_event
138 = new simple_diagnostic_event (loc, fndecl, depth, pp_formatted_text (pp),
139 thread_id);
140 m_events.safe_push (obj: new_event);
141
142 pp_clear_output_area (pp);
143
144 return diagnostic_event_id_t (m_events.length () - 1);
145}
146
147/* Mark the most recent event on this path (which must exist) as being
148 connected to the next one to be added. */
149
150void
151simple_diagnostic_path::connect_to_next_event ()
152{
153 gcc_assert (m_events.length () > 0);
154 m_events[m_events.length () - 1]->connect_to_next_event ();
155}
156
157/* struct simple_diagnostic_event. */
158
159/* simple_diagnostic_event's ctor. */
160
161simple_diagnostic_event::
162simple_diagnostic_event (location_t loc,
163 tree fndecl,
164 int depth,
165 const char *desc,
166 diagnostic_thread_id_t thread_id)
167: m_loc (loc), m_fndecl (fndecl),
168 m_logical_loc (tree_logical_location_manager::key_from_tree (node: fndecl)),
169 m_depth (depth), m_desc (xstrdup (desc)),
170 m_connected_to_next_event (false),
171 m_thread_id (thread_id)
172{
173}
174
175/* simple_diagnostic_event's dtor. */
176
177simple_diagnostic_event::~simple_diagnostic_event ()
178{
179 free (ptr: m_desc);
180}
181
182void
183simple_diagnostic_event::print_desc (pretty_printer &pp) const
184{
185 pp_string (&pp, m_desc);
186}
187
188#if CHECKING_P
189
190namespace selftest {
191
192static void
193test_intraprocedural_path (pretty_printer *event_pp)
194{
195 tree_logical_location_manager mgr;
196 tree fntype_void_void
197 = build_function_type_array (void_type_node, 0, NULL);
198 tree fndecl_foo = build_fn_decl ("foo", fntype_void_void);
199
200 simple_diagnostic_path path (mgr, event_pp);
201 path.add_event (UNKNOWN_LOCATION, fndecl: fndecl_foo, depth: 0, fmt: "first %qs", "free");
202 path.add_event (UNKNOWN_LOCATION, fndecl: fndecl_foo, depth: 0, fmt: "double %qs", "free");
203
204 ASSERT_EQ (path.num_events (), 2);
205 ASSERT_EQ (path.num_threads (), 1);
206 ASSERT_FALSE (path.interprocedural_p ());
207 ASSERT_STREQ (path.get_event (0).get_desc (*event_pp).get (),
208 "first `free'");
209 ASSERT_STREQ (path.get_event (1).get_desc (*event_pp).get (),
210 "double `free'");
211}
212
213/* Run all of the selftests within this file. */
214
215void
216simple_diagnostic_path_cc_tests ()
217{
218 /* In a few places we use the global dc's printer to determine
219 colorization so ensure this off during the tests. */
220 pretty_printer *global_pp = global_dc->get_reference_printer ();
221 const bool saved_show_color = pp_show_color (pp: global_pp);
222 pp_show_color (pp: global_pp) = false;
223
224 auto_fix_quotes fix_quotes;
225 std::unique_ptr<pretty_printer> event_pp
226 = std::unique_ptr<pretty_printer> (global_pp->clone ());
227
228 test_intraprocedural_path (event_pp: event_pp.get ());
229
230 pp_show_color (pp: global_pp) = saved_show_color;
231}
232
233} // namespace selftest
234
235#endif /* #if CHECKING_P */
236

source code of gcc/simple-diagnostic-path.cc