Branch data Line data Source code
1 : : /* Paths through the code associated with a diagnostic.
2 : : Copyright (C) 2019-2024 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_VECTOR
23 : : #include "system.h"
24 : : #include "coretypes.h"
25 : : #include "tree.h"
26 : : #include "diagnostic.h"
27 : : #include "tree-pretty-print.h"
28 : : #include "gimple-pretty-print.h"
29 : : #include "tree-diagnostic.h"
30 : : #include "langhooks.h"
31 : : #include "intl.h"
32 : : #include "diagnostic-path.h"
33 : : #include "json.h"
34 : : #include "gcc-rich-location.h"
35 : : #include "diagnostic-color.h"
36 : : #include "diagnostic-event-id.h"
37 : : #include "selftest.h"
38 : : #include "selftest-diagnostic.h"
39 : :
40 : : /* Anonymous namespace for path-printing code. */
41 : :
42 : : namespace {
43 : :
44 : : /* Subclass of range_label for showing a particular event
45 : : when showing a consecutive run of events within a diagnostic_path as
46 : : labelled ranges within one gcc_rich_location. */
47 : :
48 : 813 : class path_label : public range_label
49 : : {
50 : : public:
51 : 813 : path_label (const diagnostic_path *path, unsigned start_idx)
52 : 813 : : m_path (path), m_start_idx (start_idx)
53 : : {}
54 : :
55 : 1511 : label_text get_text (unsigned range_idx) const final override
56 : : {
57 : 1511 : unsigned event_idx = m_start_idx + range_idx;
58 : 1511 : const diagnostic_event &event = m_path->get_event (event_idx);
59 : :
60 : : /* Get the description of the event, perhaps with colorization:
61 : : normally, we don't colorize within a range_label, but this
62 : : is special-cased for diagnostic paths. */
63 : 1511 : bool colorize = pp_show_color (global_dc->printer);
64 : 1511 : label_text event_text (event.get_desc (colorize));
65 : 1511 : gcc_assert (event_text.get ());
66 : 1511 : pretty_printer pp;
67 : 1511 : pp_show_color (&pp) = pp_show_color (global_dc->printer);
68 : 1511 : diagnostic_event_id_t event_id (event_idx);
69 : 1511 : pp_printf (&pp, "%@ %s", &event_id, event_text.get ());
70 : 1511 : label_text result = label_text::take (xstrdup (pp_formatted_text (&pp)));
71 : 3022 : return result;
72 : 1511 : }
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
83 : 1394 : can_consolidate_events (const diagnostic_event &e1,
84 : : const diagnostic_event &e2,
85 : : bool check_locations)
86 : : {
87 : 1394 : if (e1.get_thread_id () != e2.get_thread_id ())
88 : : return false;
89 : :
90 : 1391 : if (e1.get_fndecl () != e2.get_fndecl ())
91 : : return false;
92 : :
93 : 940 : if (e1.get_stack_depth () != e2.get_stack_depth ())
94 : : return false;
95 : :
96 : 919 : if (check_locations)
97 : : {
98 : 855 : location_t loc1 = e1.get_location ();
99 : 855 : location_t loc2 = e2.get_location ();
100 : :
101 : 855 : if (loc1 < RESERVED_LOCATION_COUNT
102 : 855 : || loc2 < RESERVED_LOCATION_COUNT)
103 : : return false;
104 : :
105 : : /* Neither can be macro-based. */
106 : 853 : if (linemap_location_from_macro_expansion_p (line_table, loc1))
107 : : return false;
108 : 813 : if (linemap_location_from_macro_expansion_p (line_table, loc2))
109 : : return false;
110 : : }
111 : :
112 : : /* Passed all the tests. */
113 : : return true;
114 : : }
115 : :
116 : : struct event_range;
117 : : struct path_summary;
118 : : class thread_event_printer;
119 : :
120 : : /* A bundle of information about all of the events in a diagnostic_path
121 : : relating to a specific path, for use by path_summary. */
122 : :
123 : : class per_thread_summary
124 : : {
125 : : public:
126 : 271 : per_thread_summary (label_text name, unsigned swimlane_idx)
127 : 271 : : m_name (std::move (name)),
128 : 271 : m_swimlane_idx (swimlane_idx),
129 : 271 : m_min_depth (INT_MAX),
130 : 271 : m_max_depth (INT_MIN)
131 : : {}
132 : :
133 : 1664 : void update_depth_limits (int stack_depth)
134 : : {
135 : 1664 : if (stack_depth < m_min_depth)
136 : 282 : m_min_depth = stack_depth;
137 : 1664 : if (stack_depth > m_max_depth)
138 : 491 : m_max_depth = stack_depth;
139 : : }
140 : :
141 : 4 : const char *get_name () const { return m_name.get (); }
142 : 813 : unsigned get_swimlane_index () const { return m_swimlane_idx; }
143 : :
144 : : private:
145 : : friend struct path_summary;
146 : : friend class thread_event_printer;
147 : :
148 : : const label_text m_name;
149 : :
150 : : /* The "swimlane index" is the order in which this per_thread_summary
151 : : was created, for use when printing the events. */
152 : : const unsigned m_swimlane_idx;
153 : :
154 : : // The event ranges specific to this thread:
155 : : auto_vec<event_range *> m_event_ranges;
156 : : int m_min_depth;
157 : : int m_max_depth;
158 : : };
159 : :
160 : : /* A range of consecutive events within a diagnostic_path, all within the
161 : : same thread, and with the same fndecl and stack_depth, and which are suitable
162 : : to print with a single call to diagnostic_show_locus. */
163 : 813 : struct event_range
164 : : {
165 : 813 : event_range (const diagnostic_path *path, unsigned start_idx,
166 : : const diagnostic_event &initial_event,
167 : : const per_thread_summary &t)
168 : 813 : : m_path (path),
169 : 813 : m_initial_event (initial_event),
170 : 813 : m_fndecl (initial_event.get_fndecl ()),
171 : 813 : m_stack_depth (initial_event.get_stack_depth ()),
172 : 813 : m_start_idx (start_idx), m_end_idx (start_idx),
173 : 813 : m_path_label (path, start_idx),
174 : 813 : m_richloc (initial_event.get_location (), &m_path_label),
175 : 813 : m_thread_id (initial_event.get_thread_id ()),
176 : 813 : m_per_thread_summary (t)
177 : 813 : {}
178 : :
179 : 1394 : bool maybe_add_event (const diagnostic_event &new_ev, unsigned idx,
180 : : bool check_rich_locations)
181 : : {
182 : 1394 : if (!can_consolidate_events (m_initial_event, new_ev,
183 : : check_rich_locations))
184 : : return false;
185 : 852 : if (check_rich_locations)
186 : 788 : if (!m_richloc.add_location_if_nearby (new_ev.get_location (),
187 : : false, &m_path_label))
188 : : return false;
189 : 851 : m_end_idx = idx;
190 : 851 : return true;
191 : : }
192 : :
193 : : /* Print the events in this range to DC, typically as a single
194 : : call to the printer's diagnostic_show_locus. */
195 : :
196 : 813 : void print (diagnostic_context *dc, pretty_printer *pp)
197 : : {
198 : 813 : location_t initial_loc = m_initial_event.get_location ();
199 : :
200 : : /* Emit a span indicating the filename (and line/column) if the
201 : : line has changed relative to the last call to
202 : : diagnostic_show_locus. */
203 : 813 : if (dc->m_source_printing.enabled)
204 : : {
205 : 813 : expanded_location exploc
206 : : = linemap_client_expand_location_to_spelling_point
207 : 813 : (line_table, initial_loc, LOCATION_ASPECT_CARET);
208 : 813 : if (exploc.file != LOCATION_FILE (dc->m_last_location))
209 : 29 : diagnostic_start_span (dc) (dc, exploc);
210 : : }
211 : :
212 : : /* If we have an UNKNOWN_LOCATION (or BUILTINS_LOCATION) as the
213 : : primary location for an event, diagnostic_show_locus won't print
214 : : anything.
215 : :
216 : : In particular the label for the event won't get printed.
217 : : Fail more gracefully in this case by showing the event
218 : : index and text, at no particular location. */
219 : 813 : if (get_pure_location (initial_loc) <= BUILTINS_LOCATION)
220 : : {
221 : 242 : for (unsigned i = m_start_idx; i <= m_end_idx; i++)
222 : : {
223 : 153 : const diagnostic_event &iter_event = m_path->get_event (i);
224 : 153 : diagnostic_event_id_t event_id (i);
225 : 153 : label_text event_text (iter_event.get_desc (true));
226 : 153 : pp_printf (pp, " %@: %s", &event_id, event_text.get ());
227 : 153 : pp_newline (pp);
228 : 153 : }
229 : : return;
230 : : }
231 : :
232 : : /* Call diagnostic_show_locus to show the events using labels. */
233 : 724 : diagnostic_show_locus (dc, &m_richloc, DK_DIAGNOSTIC_PATH, pp);
234 : :
235 : : /* If we have a macro expansion, show the expansion to the user. */
236 : 724 : if (linemap_location_from_macro_expansion_p (line_table, initial_loc))
237 : : {
238 : 52 : gcc_assert (m_start_idx == m_end_idx);
239 : 52 : maybe_unwind_expanded_macro_loc (dc, initial_loc);
240 : : }
241 : : }
242 : :
243 : : const diagnostic_path *m_path;
244 : : const diagnostic_event &m_initial_event;
245 : : tree m_fndecl;
246 : : int m_stack_depth;
247 : : unsigned m_start_idx;
248 : : unsigned m_end_idx;
249 : : path_label m_path_label;
250 : : gcc_rich_location m_richloc;
251 : : diagnostic_thread_id_t m_thread_id;
252 : : const per_thread_summary &m_per_thread_summary;
253 : : };
254 : :
255 : : /* A struct for grouping together the events in a diagnostic_path into
256 : : ranges of events, partitioned by thread and by stack frame (i.e. by fndecl
257 : : and stack depth). */
258 : :
259 : : struct path_summary
260 : : {
261 : : path_summary (const diagnostic_path &path, bool check_rich_locations);
262 : :
263 : 40 : unsigned get_num_ranges () const { return m_ranges.length (); }
264 : 2439 : bool multithreaded_p () const { return m_per_thread_summary.length () > 1; }
265 : :
266 : : const per_thread_summary &get_events_for_thread_id (diagnostic_thread_id_t tid)
267 : : {
268 : : per_thread_summary **slot = m_thread_id_to_events.get (tid);
269 : : gcc_assert (slot);
270 : : gcc_assert (*slot);
271 : : return **slot;
272 : : }
273 : :
274 : : auto_delete_vec <event_range> m_ranges;
275 : : auto_delete_vec <per_thread_summary> m_per_thread_summary;
276 : : hash_map<int_hash<diagnostic_thread_id_t, -1, -2>,
277 : : per_thread_summary *> m_thread_id_to_events;
278 : :
279 : : private:
280 : : per_thread_summary &
281 : 1664 : get_or_create_events_for_thread_id (const diagnostic_path &path,
282 : : diagnostic_thread_id_t tid)
283 : : {
284 : 1664 : if (per_thread_summary **slot = m_thread_id_to_events.get (tid))
285 : 1393 : return **slot;
286 : :
287 : 271 : const diagnostic_thread &thread = path.get_thread (tid);
288 : 271 : per_thread_summary *pts = new per_thread_summary (thread.get_name (false),
289 : 272 : m_per_thread_summary.length ());
290 : 271 : m_thread_id_to_events.put (tid, pts);
291 : 271 : m_per_thread_summary.safe_push (pts);
292 : 271 : return *pts;
293 : : }
294 : : };
295 : :
296 : : /* path_summary's ctor. */
297 : :
298 : 274 : path_summary::path_summary (const diagnostic_path &path,
299 : 274 : bool check_rich_locations)
300 : : {
301 : 274 : const unsigned num_events = path.num_events ();
302 : :
303 : 274 : event_range *cur_event_range = NULL;
304 : 1938 : for (unsigned idx = 0; idx < num_events; idx++)
305 : : {
306 : 1664 : const diagnostic_event &event = path.get_event (idx);
307 : 1664 : const diagnostic_thread_id_t thread_id = event.get_thread_id ();
308 : 1664 : per_thread_summary &pts
309 : 1664 : = get_or_create_events_for_thread_id (path, thread_id);
310 : :
311 : 1664 : pts.update_depth_limits (event.get_stack_depth ());
312 : :
313 : 1664 : if (cur_event_range)
314 : 1394 : if (cur_event_range->maybe_add_event (event, idx, check_rich_locations))
315 : 851 : continue;
316 : :
317 : 813 : cur_event_range = new event_range (&path, idx, event, pts);
318 : 813 : m_ranges.safe_push (cur_event_range);
319 : 813 : pts.m_event_ranges.safe_push (cur_event_range);
320 : : }
321 : 274 : }
322 : :
323 : : /* Write SPACES to PP. */
324 : :
325 : : static void
326 : 3490 : write_indent (pretty_printer *pp, int spaces)
327 : : {
328 : 30284 : for (int i = 0; i < spaces; i++)
329 : 26794 : pp_space (pp);
330 : 0 : }
331 : :
332 : : /* Print FNDDECL to PP, quoting it if QUOTED is true.
333 : :
334 : : We can't use "%qE" here since we can't guarantee the capabilities
335 : : of PP. */
336 : :
337 : : static void
338 : 798 : print_fndecl (pretty_printer *pp, tree fndecl, bool quoted)
339 : : {
340 : 798 : const char *n = DECL_NAME (fndecl)
341 : 798 : ? identifier_to_locale (lang_hooks.decl_printable_name (fndecl, 2))
342 : 0 : : _("<anonymous>");
343 : 798 : if (quoted)
344 : 798 : pp_printf (pp, "%qs", n);
345 : : else
346 : 0 : pp_string (pp, n);
347 : 798 : }
348 : :
349 : : static const int base_indent = 2;
350 : : static const int per_frame_indent = 2;
351 : :
352 : : /* A bundle of state for printing event_range instances for a particular
353 : : thread. */
354 : :
355 : 272 : class thread_event_printer
356 : : {
357 : : public:
358 : 271 : thread_event_printer (const per_thread_summary &t, bool show_depths)
359 : 271 : : m_per_thread_summary (t),
360 : 271 : m_show_depths (show_depths),
361 : 271 : m_cur_indent (base_indent),
362 : 271 : m_vbar_column_for_depth (),
363 : 271 : m_num_printed (0)
364 : : {
365 : 271 : }
366 : :
367 : : /* Get the previous event_range within this thread, if any. */
368 : 813 : const event_range *get_any_prev_range () const
369 : : {
370 : 813 : if (m_num_printed > 0)
371 : 542 : return m_per_thread_summary.m_event_ranges[m_num_printed - 1];
372 : : else
373 : : return nullptr;
374 : : }
375 : :
376 : : /* Get the next event_range within this thread, if any. */
377 : 813 : const event_range *get_any_next_range () const
378 : : {
379 : 1626 : if (m_num_printed < m_per_thread_summary.m_event_ranges.length () - 1)
380 : 542 : return m_per_thread_summary.m_event_ranges[m_num_printed + 1];
381 : : else
382 : : return nullptr;
383 : : }
384 : :
385 : 813 : void print_swimlane_for_event_range (diagnostic_context *dc,
386 : : pretty_printer *pp,
387 : : event_range *range)
388 : : {
389 : 813 : const char *const line_color = "path";
390 : 813 : const char *start_line_color
391 : 813 : = colorize_start (pp_show_color (pp), line_color);
392 : 813 : const char *end_line_color = colorize_stop (pp_show_color (pp));
393 : :
394 : 813 : write_indent (pp, m_cur_indent);
395 : 813 : if (const event_range *prev_range = get_any_prev_range ())
396 : : {
397 : 542 : if (range->m_stack_depth > prev_range->m_stack_depth)
398 : : {
399 : : /* Show pushed stack frame(s). */
400 : 342 : const char *push_prefix = "+--> ";
401 : 342 : pp_string (pp, start_line_color);
402 : 342 : pp_string (pp, push_prefix);
403 : 342 : pp_string (pp, end_line_color);
404 : 342 : m_cur_indent += strlen (push_prefix);
405 : : }
406 : : }
407 : 813 : if (range->m_fndecl)
408 : : {
409 : 798 : print_fndecl (pp, range->m_fndecl, true);
410 : 798 : pp_string (pp, ": ");
411 : : }
412 : 813 : if (range->m_start_idx == range->m_end_idx)
413 : 256 : pp_printf (pp, "event %i",
414 : : range->m_start_idx + 1);
415 : : else
416 : 557 : pp_printf (pp, "events %i-%i",
417 : : range->m_start_idx + 1, range->m_end_idx + 1);
418 : 813 : if (m_show_depths)
419 : 289 : pp_printf (pp, " (depth %i)", range->m_stack_depth);
420 : 813 : pp_newline (pp);
421 : :
422 : : /* Print a run of events. */
423 : 813 : {
424 : 813 : write_indent (pp, m_cur_indent + per_frame_indent);
425 : 813 : pp_string (pp, start_line_color);
426 : 813 : pp_string (pp, "|");
427 : 813 : pp_string (pp, end_line_color);
428 : 813 : pp_newline (pp);
429 : :
430 : 813 : char *saved_prefix = pp_take_prefix (pp);
431 : 813 : char *prefix;
432 : 813 : {
433 : 813 : pretty_printer tmp_pp;
434 : 813 : write_indent (&tmp_pp, m_cur_indent + per_frame_indent);
435 : 813 : pp_string (&tmp_pp, start_line_color);
436 : 813 : pp_string (&tmp_pp, "|");
437 : 813 : pp_string (&tmp_pp, end_line_color);
438 : 813 : prefix = xstrdup (pp_formatted_text (&tmp_pp));
439 : 813 : }
440 : 813 : pp_set_prefix (pp, prefix);
441 : 813 : pp_prefixing_rule (pp) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
442 : 813 : range->print (dc, pp);
443 : 813 : pp_set_prefix (pp, saved_prefix);
444 : :
445 : 813 : write_indent (pp, m_cur_indent + per_frame_indent);
446 : 813 : pp_string (pp, start_line_color);
447 : 813 : pp_string (pp, "|");
448 : 813 : pp_string (pp, end_line_color);
449 : 813 : pp_newline (pp);
450 : : }
451 : :
452 : 813 : if (const event_range *next_range = get_any_next_range ())
453 : : {
454 : 542 : if (range->m_stack_depth > next_range->m_stack_depth)
455 : : {
456 : 130 : if (m_vbar_column_for_depth.get (next_range->m_stack_depth))
457 : : {
458 : : /* Show returning from stack frame(s), by printing
459 : : something like:
460 : : " |\n"
461 : : " <------------ +\n"
462 : : " |\n". */
463 : 119 : int vbar_for_next_frame
464 : 119 : = *m_vbar_column_for_depth.get (next_range->m_stack_depth);
465 : :
466 : 119 : int indent_for_next_frame
467 : : = vbar_for_next_frame - per_frame_indent;
468 : 119 : write_indent (pp, vbar_for_next_frame);
469 : 119 : pp_string (pp, start_line_color);
470 : 119 : pp_character (pp, '<');
471 : 1050 : for (int i = indent_for_next_frame + per_frame_indent;
472 : 1050 : i < m_cur_indent + per_frame_indent - 1; i++)
473 : 931 : pp_character (pp, '-');
474 : 119 : pp_character (pp, '+');
475 : 119 : pp_string (pp, end_line_color);
476 : 119 : pp_newline (pp);
477 : 119 : m_cur_indent = indent_for_next_frame;
478 : :
479 : 119 : write_indent (pp, vbar_for_next_frame);
480 : 119 : pp_string (pp, start_line_color);
481 : 119 : pp_character (pp, '|');
482 : 119 : pp_string (pp, end_line_color);
483 : 119 : pp_newline (pp);
484 : : }
485 : : else
486 : : {
487 : : /* Handle disjoint paths (e.g. a callback at some later
488 : : time). */
489 : 11 : m_cur_indent = base_indent;
490 : : }
491 : : }
492 : 412 : else if (range->m_stack_depth < next_range->m_stack_depth)
493 : : {
494 : : /* Prepare to show pushed stack frame. */
495 : 342 : gcc_assert (range->m_stack_depth != EMPTY);
496 : 342 : gcc_assert (range->m_stack_depth != DELETED);
497 : 342 : m_vbar_column_for_depth.put (range->m_stack_depth,
498 : 342 : m_cur_indent + per_frame_indent);
499 : 342 : m_cur_indent += per_frame_indent;
500 : : }
501 : : }
502 : :
503 : 813 : m_num_printed++;
504 : 813 : }
505 : :
506 : : int get_cur_indent () const { return m_cur_indent; }
507 : :
508 : : private:
509 : : const per_thread_summary &m_per_thread_summary;
510 : : bool m_show_depths;
511 : :
512 : : /* Print the ranges. */
513 : : int m_cur_indent;
514 : :
515 : : /* Keep track of column numbers of existing '|' characters for
516 : : stack depths we've already printed. */
517 : : static const int EMPTY = -1;
518 : : static const int DELETED = -2;
519 : : typedef int_hash <int, EMPTY, DELETED> vbar_hash;
520 : : hash_map <vbar_hash, int> m_vbar_column_for_depth;
521 : :
522 : : /* How many event ranges within this swimlane have we printed.
523 : : This is the index of the next event_range to print. */
524 : : unsigned m_num_printed;
525 : : };
526 : :
527 : : /* Print path_summary PS to DC, giving an overview of the interprocedural
528 : : calls and returns.
529 : :
530 : : Print the event descriptions in a nested form, printing the event
531 : : descriptions within calls to diagnostic_show_locus, using labels to
532 : : show the events:
533 : :
534 : : 'foo' (events 1-2)
535 : : | NN |
536 : : | |
537 : : +--> 'bar' (events 3-4)
538 : : | NN |
539 : : | |
540 : : +--> 'baz' (events 5-6)
541 : : | NN |
542 : : | |
543 : : <------------ +
544 : : |
545 : : 'foo' (events 7-8)
546 : : | NN |
547 : : | |
548 : : +--> 'bar' (events 9-10)
549 : : | NN |
550 : : | |
551 : : +--> 'baz' (events 11-12)
552 : : | NN |
553 : : | |
554 : :
555 : : If SHOW_DEPTHS is true, append " (depth N)" to the header of each run
556 : : of events.
557 : :
558 : : For events with UNKNOWN_LOCATION, print a summary of each the event. */
559 : :
560 : : static void
561 : 274 : print_path_summary_as_text (const path_summary *ps, diagnostic_context *dc,
562 : : bool show_depths)
563 : : {
564 : 274 : pretty_printer *pp = dc->printer;
565 : :
566 : 274 : std::vector<thread_event_printer> thread_event_printers;
567 : 1085 : for (auto t : ps->m_per_thread_summary)
568 : 271 : thread_event_printers.push_back (thread_event_printer (*t, show_depths));
569 : :
570 : : unsigned i;
571 : : event_range *range;
572 : 1087 : FOR_EACH_VEC_ELT (ps->m_ranges, i, range)
573 : : {
574 : 813 : const int swimlane_idx
575 : 813 : = range->m_per_thread_summary.get_swimlane_index ();
576 : 813 : if (ps->multithreaded_p ())
577 : 6 : if (i == 0 || ps->m_ranges[i - 1]->m_thread_id != range->m_thread_id)
578 : : {
579 : 4 : if (i > 0)
580 : 3 : pp_newline (pp);
581 : 4 : pp_printf (pp, "Thread: %qs",
582 : 4 : range->m_per_thread_summary.get_name ());
583 : 4 : pp_newline (pp);
584 : : }
585 : 813 : thread_event_printer &tep = thread_event_printers[swimlane_idx];
586 : 813 : tep.print_swimlane_for_event_range (dc, pp, range);
587 : : }
588 : 274 : }
589 : :
590 : : } /* end of anonymous namespace for path-printing code. */
591 : :
592 : : /* Print PATH to CONTEXT, according to CONTEXT's path_format. */
593 : :
594 : : void
595 : 4418 : default_tree_diagnostic_path_printer (diagnostic_context *context,
596 : : const diagnostic_path *path)
597 : : {
598 : 4418 : gcc_assert (path);
599 : :
600 : 4418 : const unsigned num_events = path->num_events ();
601 : :
602 : 4418 : switch (context->get_path_format ())
603 : : {
604 : : case DPF_NONE:
605 : : /* Do nothing. */
606 : : return;
607 : :
608 : : case DPF_SEPARATE_EVENTS:
609 : : {
610 : : /* A note per event. */
611 : 21107 : for (unsigned i = 0; i < num_events; i++)
612 : : {
613 : 16949 : const diagnostic_event &event = path->get_event (i);
614 : 16949 : label_text event_text (event.get_desc (false));
615 : 16949 : gcc_assert (event_text.get ());
616 : 16949 : diagnostic_event_id_t event_id (i);
617 : 16949 : if (context->show_path_depths_p ())
618 : : {
619 : 215 : int stack_depth = event.get_stack_depth ();
620 : 215 : tree fndecl = event.get_fndecl ();
621 : : /* -fdiagnostics-path-format=separate-events doesn't print
622 : : fndecl information, so with -fdiagnostics-show-path-depths
623 : : print the fndecls too, if any. */
624 : 215 : if (fndecl)
625 : 215 : inform (event.get_location (),
626 : : "%@ %s (fndecl %qD, depth %i)",
627 : : &event_id, event_text.get (),
628 : : fndecl, stack_depth);
629 : : else
630 : 0 : inform (event.get_location (),
631 : : "%@ %s (depth %i)",
632 : : &event_id, event_text.get (),
633 : : stack_depth);
634 : : }
635 : : else
636 : 16734 : inform (event.get_location (),
637 : : "%@ %s", &event_id, event_text.get ());
638 : 16949 : }
639 : : }
640 : : break;
641 : :
642 : 254 : case DPF_INLINE_EVENTS:
643 : 254 : {
644 : : /* Consolidate related events. */
645 : 254 : path_summary summary (*path, true);
646 : 254 : char *saved_prefix = pp_take_prefix (context->printer);
647 : 254 : pp_set_prefix (context->printer, NULL);
648 : 254 : print_path_summary_as_text (&summary, context,
649 : 254 : context->show_path_depths_p ());
650 : 254 : pp_flush (context->printer);
651 : 254 : pp_set_prefix (context->printer, saved_prefix);
652 : 254 : }
653 : 254 : break;
654 : : }
655 : : }
656 : :
657 : : /* This has to be here, rather than diagnostic-format-json.cc,
658 : : since diagnostic-format-json.o is within OBJS-libcommon and thus
659 : : doesn't have access to trees (for m_fndecl). */
660 : :
661 : : json::value *
662 : 6 : default_tree_make_json_for_path (diagnostic_context *context,
663 : : const diagnostic_path *path)
664 : : {
665 : 6 : json::array *path_array = new json::array ();
666 : 19 : for (unsigned i = 0; i < path->num_events (); i++)
667 : : {
668 : 13 : const diagnostic_event &event = path->get_event (i);
669 : :
670 : 13 : json::object *event_obj = new json::object ();
671 : 13 : if (event.get_location ())
672 : 13 : event_obj->set ("location",
673 : : json_from_expanded_location (context,
674 : 13 : event.get_location ()));
675 : 13 : label_text event_text (event.get_desc (false));
676 : 13 : event_obj->set_string ("description", event_text.get ());
677 : 13 : if (tree fndecl = event.get_fndecl ())
678 : : {
679 : 8 : const char *function
680 : 8 : = identifier_to_locale (lang_hooks.decl_printable_name (fndecl, 2));
681 : 8 : event_obj->set_string ("function", function);
682 : : }
683 : 13 : event_obj->set_integer ("depth", event.get_stack_depth ());
684 : 13 : path_array->append (event_obj);
685 : 13 : }
686 : 6 : return path_array;
687 : : }
688 : :
689 : : #if CHECKING_P
690 : :
691 : : /* Disable warnings about missing quoting in GCC diagnostics for the print
692 : : calls in the tests below. */
693 : : #if __GNUC__ >= 10
694 : : # pragma GCC diagnostic push
695 : : # pragma GCC diagnostic ignored "-Wformat-diag"
696 : : #endif
697 : :
698 : : namespace selftest {
699 : :
700 : : /* A subclass of simple_diagnostic_path that adds member functions
701 : : for adding test events. */
702 : :
703 : 40 : class test_diagnostic_path : public simple_diagnostic_path
704 : : {
705 : : public:
706 : 20 : test_diagnostic_path (pretty_printer *event_pp)
707 : 40 : : simple_diagnostic_path (event_pp)
708 : : {
709 : : }
710 : :
711 : 60 : void add_entry (tree fndecl, int stack_depth)
712 : : {
713 : 60 : add_event (UNKNOWN_LOCATION, fndecl, stack_depth,
714 : : "entering %qE", fndecl);
715 : : }
716 : :
717 : 12 : void add_return (tree fndecl, int stack_depth)
718 : : {
719 : 12 : add_event (UNKNOWN_LOCATION, fndecl, stack_depth,
720 : : "returning to %qE", fndecl);
721 : : }
722 : :
723 : 48 : void add_call (tree caller, int caller_stack_depth, tree callee)
724 : : {
725 : 48 : add_event (UNKNOWN_LOCATION, caller, caller_stack_depth,
726 : : "calling %qE", callee);
727 : 48 : add_entry (callee, caller_stack_depth + 1);
728 : 48 : }
729 : : };
730 : :
731 : : /* Verify that empty paths are handled gracefully. */
732 : :
733 : : static void
734 : 4 : test_empty_path (pretty_printer *event_pp)
735 : : {
736 : 4 : test_diagnostic_path path (event_pp);
737 : 4 : ASSERT_FALSE (path.interprocedural_p ());
738 : :
739 : 4 : path_summary summary (path, false);
740 : 4 : ASSERT_EQ (summary.get_num_ranges (), 0);
741 : :
742 : 4 : test_diagnostic_context dc;
743 : 4 : print_path_summary_as_text (&summary, &dc, true);
744 : 4 : ASSERT_STREQ ("",
745 : : pp_formatted_text (dc.printer));
746 : 4 : }
747 : :
748 : : /* Verify that print_path_summary works on a purely intraprocedural path. */
749 : :
750 : : static void
751 : 4 : test_intraprocedural_path (pretty_printer *event_pp)
752 : : {
753 : 4 : tree fntype_void_void
754 : 4 : = build_function_type_array (void_type_node, 0, NULL);
755 : 4 : tree fndecl_foo = build_fn_decl ("foo", fntype_void_void);
756 : :
757 : 4 : test_diagnostic_path path (event_pp);
758 : 4 : path.add_event (UNKNOWN_LOCATION, fndecl_foo, 0, "first %qs", "free");
759 : 4 : path.add_event (UNKNOWN_LOCATION, fndecl_foo, 0, "double %qs", "free");
760 : :
761 : 4 : ASSERT_FALSE (path.interprocedural_p ());
762 : :
763 : 4 : path_summary summary (path, false);
764 : 4 : ASSERT_EQ (summary.get_num_ranges (), 1);
765 : :
766 : 4 : test_diagnostic_context dc;
767 : 4 : print_path_summary_as_text (&summary, &dc, true);
768 : 4 : ASSERT_STREQ (" `foo': events 1-2 (depth 0)\n"
769 : : " |\n"
770 : : " | (1): first `free'\n"
771 : : " | (2): double `free'\n"
772 : : " |\n",
773 : : pp_formatted_text (dc.printer));
774 : 4 : }
775 : :
776 : : /* Verify that print_path_summary works on an interprocedural path. */
777 : :
778 : : static void
779 : 4 : test_interprocedural_path_1 (pretty_printer *event_pp)
780 : : {
781 : : /* Build fndecls. The types aren't quite right, but that
782 : : doesn't matter for the purposes of this test. */
783 : 4 : tree fntype_void_void
784 : 4 : = build_function_type_array (void_type_node, 0, NULL);
785 : 4 : tree fndecl_test = build_fn_decl ("test", fntype_void_void);
786 : 4 : tree fndecl_make_boxed_int
787 : 4 : = build_fn_decl ("make_boxed_int", fntype_void_void);
788 : 4 : tree fndecl_wrapped_malloc
789 : 4 : = build_fn_decl ("wrapped_malloc", fntype_void_void);
790 : 4 : tree fndecl_free_boxed_int
791 : 4 : = build_fn_decl ("free_boxed_int", fntype_void_void);
792 : 4 : tree fndecl_wrapped_free
793 : 4 : = build_fn_decl ("wrapped_free", fntype_void_void);
794 : :
795 : 4 : test_diagnostic_path path (event_pp);
796 : 4 : path.add_entry (fndecl_test, 0);
797 : 4 : path.add_call (fndecl_test, 0, fndecl_make_boxed_int);
798 : 4 : path.add_call (fndecl_make_boxed_int, 1, fndecl_wrapped_malloc);
799 : 4 : path.add_event (UNKNOWN_LOCATION, fndecl_wrapped_malloc, 2, "calling malloc");
800 : 4 : path.add_return (fndecl_test, 0);
801 : 4 : path.add_call (fndecl_test, 0, fndecl_free_boxed_int);
802 : 4 : path.add_call (fndecl_free_boxed_int, 1, fndecl_wrapped_free);
803 : 4 : path.add_event (UNKNOWN_LOCATION, fndecl_wrapped_free, 2, "calling free");
804 : 4 : path.add_return (fndecl_test, 0);
805 : 4 : path.add_call (fndecl_test, 0, fndecl_free_boxed_int);
806 : 4 : path.add_call (fndecl_free_boxed_int, 1, fndecl_wrapped_free);
807 : 4 : path.add_event (UNKNOWN_LOCATION, fndecl_wrapped_free, 2, "calling free");
808 : 4 : ASSERT_EQ (path.num_events (), 18);
809 : :
810 : 4 : ASSERT_TRUE (path.interprocedural_p ());
811 : :
812 : 4 : path_summary summary (path, false);
813 : 4 : ASSERT_EQ (summary.get_num_ranges (), 9);
814 : :
815 : 4 : test_diagnostic_context dc;
816 : 4 : print_path_summary_as_text (&summary, &dc, true);
817 : 4 : ASSERT_STREQ
818 : : (" `test': events 1-2 (depth 0)\n"
819 : : " |\n"
820 : : " | (1): entering `test'\n"
821 : : " | (2): calling `make_boxed_int'\n"
822 : : " |\n"
823 : : " +--> `make_boxed_int': events 3-4 (depth 1)\n"
824 : : " |\n"
825 : : " | (3): entering `make_boxed_int'\n"
826 : : " | (4): calling `wrapped_malloc'\n"
827 : : " |\n"
828 : : " +--> `wrapped_malloc': events 5-6 (depth 2)\n"
829 : : " |\n"
830 : : " | (5): entering `wrapped_malloc'\n"
831 : : " | (6): calling malloc\n"
832 : : " |\n"
833 : : " <-------------+\n"
834 : : " |\n"
835 : : " `test': events 7-8 (depth 0)\n"
836 : : " |\n"
837 : : " | (7): returning to `test'\n"
838 : : " | (8): calling `free_boxed_int'\n"
839 : : " |\n"
840 : : " +--> `free_boxed_int': events 9-10 (depth 1)\n"
841 : : " |\n"
842 : : " | (9): entering `free_boxed_int'\n"
843 : : " | (10): calling `wrapped_free'\n"
844 : : " |\n"
845 : : " +--> `wrapped_free': events 11-12 (depth 2)\n"
846 : : " |\n"
847 : : " | (11): entering `wrapped_free'\n"
848 : : " | (12): calling free\n"
849 : : " |\n"
850 : : " <-------------+\n"
851 : : " |\n"
852 : : " `test': events 13-14 (depth 0)\n"
853 : : " |\n"
854 : : " | (13): returning to `test'\n"
855 : : " | (14): calling `free_boxed_int'\n"
856 : : " |\n"
857 : : " +--> `free_boxed_int': events 15-16 (depth 1)\n"
858 : : " |\n"
859 : : " | (15): entering `free_boxed_int'\n"
860 : : " | (16): calling `wrapped_free'\n"
861 : : " |\n"
862 : : " +--> `wrapped_free': events 17-18 (depth 2)\n"
863 : : " |\n"
864 : : " | (17): entering `wrapped_free'\n"
865 : : " | (18): calling free\n"
866 : : " |\n",
867 : : pp_formatted_text (dc.printer));
868 : 4 : }
869 : :
870 : : /* Example where we pop the stack to an intermediate frame, rather than the
871 : : initial one. */
872 : :
873 : : static void
874 : 4 : test_interprocedural_path_2 (pretty_printer *event_pp)
875 : : {
876 : : /* Build fndecls. The types aren't quite right, but that
877 : : doesn't matter for the purposes of this test. */
878 : 4 : tree fntype_void_void
879 : 4 : = build_function_type_array (void_type_node, 0, NULL);
880 : 4 : tree fndecl_foo = build_fn_decl ("foo", fntype_void_void);
881 : 4 : tree fndecl_bar = build_fn_decl ("bar", fntype_void_void);
882 : 4 : tree fndecl_baz = build_fn_decl ("baz", fntype_void_void);
883 : :
884 : 4 : test_diagnostic_path path (event_pp);
885 : 4 : path.add_entry (fndecl_foo, 0);
886 : 4 : path.add_call (fndecl_foo, 0, fndecl_bar);
887 : 4 : path.add_call (fndecl_bar, 1, fndecl_baz);
888 : 4 : path.add_return (fndecl_bar, 1);
889 : 4 : path.add_call (fndecl_bar, 1, fndecl_baz);
890 : 4 : ASSERT_EQ (path.num_events (), 8);
891 : :
892 : 4 : ASSERT_TRUE (path.interprocedural_p ());
893 : :
894 : 4 : path_summary summary (path, false);
895 : 4 : ASSERT_EQ (summary.get_num_ranges (), 5);
896 : :
897 : 4 : test_diagnostic_context dc;
898 : 4 : print_path_summary_as_text (&summary, &dc, true);
899 : 4 : ASSERT_STREQ
900 : : (" `foo': events 1-2 (depth 0)\n"
901 : : " |\n"
902 : : " | (1): entering `foo'\n"
903 : : " | (2): calling `bar'\n"
904 : : " |\n"
905 : : " +--> `bar': events 3-4 (depth 1)\n"
906 : : " |\n"
907 : : " | (3): entering `bar'\n"
908 : : " | (4): calling `baz'\n"
909 : : " |\n"
910 : : " +--> `baz': event 5 (depth 2)\n"
911 : : " |\n"
912 : : " | (5): entering `baz'\n"
913 : : " |\n"
914 : : " <------+\n"
915 : : " |\n"
916 : : " `bar': events 6-7 (depth 1)\n"
917 : : " |\n"
918 : : " | (6): returning to `bar'\n"
919 : : " | (7): calling `baz'\n"
920 : : " |\n"
921 : : " +--> `baz': event 8 (depth 2)\n"
922 : : " |\n"
923 : : " | (8): entering `baz'\n"
924 : : " |\n",
925 : : pp_formatted_text (dc.printer));
926 : 4 : }
927 : :
928 : : /* Verify that print_path_summary is sane in the face of a recursive
929 : : diagnostic_path. */
930 : :
931 : : static void
932 : 4 : test_recursion (pretty_printer *event_pp)
933 : : {
934 : 4 : tree fntype_void_void
935 : 4 : = build_function_type_array (void_type_node, 0, NULL);
936 : 4 : tree fndecl_factorial = build_fn_decl ("factorial", fntype_void_void);
937 : :
938 : 4 : test_diagnostic_path path (event_pp);
939 : 4 : path.add_entry (fndecl_factorial, 0);
940 : 16 : for (int depth = 0; depth < 3; depth++)
941 : 12 : path.add_call (fndecl_factorial, depth, fndecl_factorial);
942 : 4 : ASSERT_EQ (path.num_events (), 7);
943 : :
944 : 4 : ASSERT_TRUE (path.interprocedural_p ());
945 : :
946 : 4 : path_summary summary (path, false);
947 : 4 : ASSERT_EQ (summary.get_num_ranges (), 4);
948 : :
949 : 4 : test_diagnostic_context dc;
950 : 4 : print_path_summary_as_text (&summary, &dc, true);
951 : 4 : ASSERT_STREQ
952 : : (" `factorial': events 1-2 (depth 0)\n"
953 : : " |\n"
954 : : " | (1): entering `factorial'\n"
955 : : " | (2): calling `factorial'\n"
956 : : " |\n"
957 : : " +--> `factorial': events 3-4 (depth 1)\n"
958 : : " |\n"
959 : : " | (3): entering `factorial'\n"
960 : : " | (4): calling `factorial'\n"
961 : : " |\n"
962 : : " +--> `factorial': events 5-6 (depth 2)\n"
963 : : " |\n"
964 : : " | (5): entering `factorial'\n"
965 : : " | (6): calling `factorial'\n"
966 : : " |\n"
967 : : " +--> `factorial': event 7 (depth 3)\n"
968 : : " |\n"
969 : : " | (7): entering `factorial'\n"
970 : : " |\n",
971 : : pp_formatted_text (dc.printer));
972 : 4 : }
973 : :
974 : : /* Run all of the selftests within this file. */
975 : :
976 : : void
977 : 4 : tree_diagnostic_path_cc_tests ()
978 : : {
979 : 4 : auto_fix_quotes fix_quotes;
980 : 4 : pretty_printer *event_pp = global_dc->printer->clone ();
981 : 4 : pp_show_color (event_pp) = 0;
982 : 4 : test_empty_path (event_pp);
983 : 4 : test_intraprocedural_path (event_pp);
984 : 4 : test_interprocedural_path_1 (event_pp);
985 : 4 : test_interprocedural_path_2 (event_pp);
986 : 4 : test_recursion (event_pp);
987 : 4 : delete event_pp;
988 : 4 : }
989 : :
990 : : } // namespace selftest
991 : :
992 : : #if __GNUC__ >= 10
993 : : # pragma GCC diagnostic pop
994 : : #endif
995 : :
996 : : #endif /* #if CHECKING_P */
|