Line data Source code
1 : /* Paths through the code associated with a diagnostic.
2 : Copyright (C) 2019-2026 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 : #define INCLUDE_ALGORITHM
23 : #define INCLUDE_MAP
24 : #define INCLUDE_STRING
25 : #define INCLUDE_VECTOR
26 : #include "system.h"
27 : #include "coretypes.h"
28 : #include "diagnostics/paths.h"
29 : #include "diagnostics/state-graphs.h"
30 :
31 : /* Disable warnings about missing quoting in GCC diagnostics for the print
32 : calls below. */
33 : #if __GNUC__ >= 10
34 : # pragma GCC diagnostic push
35 : # pragma GCC diagnostic ignored "-Wformat-diag"
36 : #endif
37 :
38 : using namespace diagnostics;
39 : using namespace diagnostics::paths;
40 :
41 : /* class diagnostics::paths::event. */
42 :
43 : /* struct event::meaning. */
44 :
45 : void
46 144 : event::meaning::dump_to_pp (pretty_printer *pp) const
47 : {
48 144 : bool need_comma = false;
49 144 : pp_character (pp, '{');
50 144 : if (const char *verb_str = maybe_get_verb_str (m_verb))
51 : {
52 112 : pp_printf (pp, "verb: %qs", verb_str);
53 112 : need_comma = true;
54 : }
55 144 : if (const char *noun_str = maybe_get_noun_str (m_noun))
56 : {
57 112 : if (need_comma)
58 112 : pp_string (pp, ", ");
59 112 : pp_printf (pp, "noun: %qs", noun_str);
60 112 : need_comma = true;
61 : }
62 144 : if (const char *property_str = maybe_get_property_str (m_property))
63 : {
64 0 : if (need_comma)
65 0 : pp_string (pp, ", ");
66 0 : pp_printf (pp, "property: %qs", property_str);
67 0 : need_comma = true;
68 : }
69 144 : pp_character (pp, '}');
70 144 : }
71 :
72 : /* Get a string (or nullptr) for V suitable for use within a SARIF
73 : threadFlowLocation "kinds" property (SARIF v2.1.0 section 3.38.8). */
74 :
75 : const char *
76 286 : event::meaning::maybe_get_verb_str (enum verb v)
77 : {
78 286 : switch (v)
79 : {
80 0 : default:
81 0 : gcc_unreachable ();
82 : case verb::unknown:
83 : return nullptr;
84 81 : case verb::acquire:
85 81 : return "acquire";
86 56 : case verb::release:
87 56 : return "release";
88 21 : case verb::enter:
89 21 : return "enter";
90 0 : case verb::exit:
91 0 : return "exit";
92 13 : case verb::call:
93 13 : return "call";
94 3 : case verb::return_:
95 3 : return "return";
96 30 : case verb::branch:
97 30 : return "branch";
98 32 : case verb::danger:
99 32 : return "danger";
100 :
101 : /* Special control flow operations.
102 :
103 : These are not part of SARIF v2.1.0 section 3.38.8, but the
104 : spec allows other values; see
105 : https://github.com/oasis-tcs/sarif-spec/issues/735 */
106 6 : case verb::throw_:
107 6 : return "throw";
108 6 : case verb::catch_:
109 6 : return "catch";
110 3 : case verb::unwind_:
111 3 : return "unwind";
112 1 : case verb::setjmp_:
113 1 : return "setjmp";
114 2 : case verb::longjmp_:
115 2 : return "longjmp";
116 : }
117 : }
118 :
119 : /* Get a string (or nullptr) for N suitable for use within a SARIF
120 : threadFlowLocation "kinds" property (SARIF v2.1.0 section 3.38.8). */
121 :
122 : const char *
123 286 : event::meaning::maybe_get_noun_str (enum noun n)
124 : {
125 286 : switch (n)
126 : {
127 0 : default:
128 0 : gcc_unreachable ();
129 : case noun::unknown:
130 : return nullptr;
131 0 : case noun::taint:
132 0 : return "taint";
133 0 : case noun::sensitive:
134 0 : return "sensitive";
135 37 : case noun::function:
136 37 : return "function";
137 0 : case noun::lock:
138 0 : return "lock";
139 41 : case noun::memory:
140 41 : return "memory";
141 96 : case noun::resource:
142 96 : return "resource";
143 : }
144 : }
145 :
146 : /* Get a string (or nullptr) for P suitable for use within a SARIF
147 : threadFlowLocation "kinds" property (SARIF v2.1.0 section 3.38.8). */
148 :
149 : const char *
150 286 : event::meaning::maybe_get_property_str (enum property p)
151 : {
152 286 : switch (p)
153 : {
154 0 : default:
155 0 : gcc_unreachable ();
156 : case property::unknown:
157 : return nullptr;
158 16 : case property::true_:
159 16 : return "true";
160 14 : case property::false_:
161 14 : return "false";
162 : }
163 : }
164 :
165 : /* Generate a label_text containing the description of this event
166 : (for debugging/logging purposes). */
167 :
168 : label_text
169 16 : event::get_desc (pretty_printer &ref_pp) const
170 : {
171 16 : auto pp = ref_pp.clone ();
172 16 : pp_show_color (pp.get ()) = false;
173 16 : print_desc (*pp.get ());
174 16 : return label_text::take (xstrdup (pp_formatted_text (pp.get ())));
175 16 : }
176 :
177 : // Base implementation of event::maybe_make_diagnostic_state_graph
178 :
179 : std::unique_ptr<digraphs::digraph>
180 0 : event::maybe_make_diagnostic_state_graph (bool) const
181 : {
182 : // Don't attempt to make a state graph:
183 0 : return nullptr;
184 : }
185 :
186 : /* class diagnostics::paths::path. */
187 :
188 : /* Subroutine of path::interprocedural_p.
189 : Look for the first event in this path that is within a function
190 : i.e. has a non-null logical location for which function_p is true.
191 : If found, write its index to *OUT_IDX and return true.
192 : Otherwise return false. */
193 :
194 : bool
195 4019 : path::get_first_event_in_a_function (unsigned *out_idx) const
196 : {
197 4019 : const unsigned num = num_events ();
198 4157 : for (unsigned i = 0; i < num; i++)
199 : {
200 4061 : const event &event = get_event (i);
201 4061 : if (logical_locations::key logical_loc = event.get_logical_location ())
202 3923 : if (m_logical_loc_mgr.function_p (logical_loc))
203 : {
204 3923 : *out_idx = i;
205 3923 : return true;
206 : }
207 : }
208 : return false;
209 : }
210 :
211 : /* Return true if the events in this path involve more than one
212 : function, or false if it is purely intraprocedural. */
213 :
214 : bool
215 4019 : path::interprocedural_p () const
216 : {
217 : /* Ignore leading events that are outside of any function. */
218 4019 : unsigned first_fn_event_idx;
219 4019 : if (!get_first_event_in_a_function (&first_fn_event_idx))
220 : return false;
221 :
222 3923 : const event &first_fn_event = get_event (first_fn_event_idx);
223 3923 : int first_fn_stack_depth = first_fn_event.get_stack_depth ();
224 :
225 3923 : const unsigned num = num_events ();
226 11838 : for (unsigned i = first_fn_event_idx + 1; i < num; i++)
227 : {
228 8704 : if (!same_function_p (first_fn_event_idx, i))
229 : return true;
230 8147 : if (get_event (i).get_stack_depth () != first_fn_stack_depth)
231 : return true;
232 : }
233 : return false;
234 : }
235 :
236 : /* Print PATH by emitting a dummy "note" associated with it. */
237 :
238 : DEBUG_FUNCTION
239 0 : void debug (path *p)
240 : {
241 0 : rich_location richloc (line_table, UNKNOWN_LOCATION);
242 0 : richloc.set_path (p);
243 0 : inform (&richloc, "debug path");
244 0 : }
245 :
246 : #if __GNUC__ >= 10
247 : # pragma GCC diagnostic pop
248 : #endif
|