Line data Source code
1 : /* Printing 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/macro-unwinding.h"
29 : #include "intl.h"
30 : #include "diagnostics/paths.h"
31 : #include "gcc-rich-location.h"
32 : #include "diagnostics/color.h"
33 : #include "diagnostics/file-cache.h"
34 : #include "diagnostics/event-id.h"
35 : #include "diagnostics/source-printing-effects.h"
36 : #include "pretty-print-markup.h"
37 : #include "selftest.h"
38 : #include "diagnostics/selftest-context.h"
39 : #include "diagnostics/selftest-paths.h"
40 : #include "text-art/theme.h"
41 : #include "diagnostics/text-sink.h"
42 : #include "diagnostics/html-sink.h"
43 : #include "xml.h"
44 : #include "xml-printer.h"
45 :
46 : /* Disable warnings about missing quoting in GCC diagnostics for the print
47 : calls below. */
48 : #if __GNUC__ >= 10
49 : # pragma GCC diagnostic push
50 : # pragma GCC diagnostic ignored "-Wformat-diag"
51 : #endif
52 :
53 : /* Anonymous namespace for path-printing code. */
54 :
55 : namespace {
56 :
57 : using namespace diagnostics;
58 : using namespace diagnostics::paths;
59 :
60 : /* A bundle of state for printing a path. */
61 :
62 : class path_print_policy
63 : {
64 : public:
65 1513 : path_print_policy (const diagnostics::text_sink &text_output)
66 1513 : : m_source_policy (text_output.get_context ())
67 : {
68 : }
69 :
70 3 : path_print_policy (const diagnostics::context &dc)
71 3 : : m_source_policy (dc)
72 : {
73 : }
74 :
75 : text_art::theme *
76 2651 : get_diagram_theme () const
77 : {
78 2651 : return m_source_policy.get_diagram_theme ();
79 : }
80 :
81 : const diagnostics::source_print_policy &
82 2301 : get_source_policy () const { return m_source_policy; }
83 :
84 : private:
85 : diagnostics::source_print_policy m_source_policy;
86 : };
87 :
88 : /* Subclass of range_label for showing a particular event
89 : when showing a consecutive run of events within a diagnostic path as
90 : labelled ranges within one gcc_rich_location. */
91 :
92 2651 : class path_label : public range_label
93 : {
94 : public:
95 2651 : path_label (const path &path_,
96 : const pretty_printer &ref_pp,
97 : unsigned start_idx,
98 : bool colorize,
99 : bool allow_emojis)
100 2651 : : m_path (path_),
101 2651 : m_ref_pp (ref_pp),
102 2651 : m_start_idx (start_idx), m_effects (*this),
103 2651 : m_colorize (colorize), m_allow_emojis (allow_emojis)
104 : {}
105 :
106 4669 : label_text get_text (unsigned range_idx) const final override
107 : {
108 4669 : unsigned event_idx = m_start_idx + range_idx;
109 4669 : const event &ev = m_path.get_event (event_idx);
110 :
111 4669 : const event::meaning meaning (ev.get_meaning ());
112 :
113 4669 : auto pp = m_ref_pp.clone ();
114 4669 : pp_show_color (pp.get ()) = m_colorize;
115 4669 : event_id_t event_id (event_idx);
116 4669 : pp_printf (pp.get (), "%@", &event_id);
117 4669 : pp_space (pp.get ());
118 :
119 4669 : if (meaning.m_verb == event::verb::danger
120 200 : && m_allow_emojis)
121 : {
122 2 : pp_unicode_character (pp.get (), 0x26A0); /* U+26A0 WARNING SIGN. */
123 : /* Append U+FE0F VARIATION SELECTOR-16 to select the emoji
124 : variation of the char. */
125 2 : pp_unicode_character (pp.get (), 0xFE0F);
126 : /* U+26A0 WARNING SIGN has East_Asian_Width == Neutral, but in its
127 : emoji variant is printed (by vte at least) with a 2nd half
128 : overlapping the next char. Hence we add two spaces here: a space
129 : to be covered by this overlap, plus another space of padding. */
130 2 : pp_string (pp.get (), " ");
131 : }
132 :
133 4669 : ev.print_desc (*pp.get ());
134 :
135 4669 : label_text result
136 4669 : = label_text::take (xstrdup (pp_formatted_text (pp.get ())));
137 9338 : return result;
138 4669 : }
139 :
140 : const diagnostics::label_effects *
141 9338 : get_effects (unsigned /*range_idx*/) const final override
142 : {
143 9338 : return &m_effects;
144 : }
145 :
146 : private:
147 2651 : class path_label_effects : public label_effects
148 : {
149 : public:
150 2651 : path_label_effects (const path_label &path_label)
151 2651 : : m_path_label (path_label)
152 : {
153 : }
154 4669 : bool has_in_edge (unsigned range_idx) const final override
155 : {
156 9338 : if (const event *prev_event
157 4669 : = m_path_label.get_prev_event (range_idx))
158 3254 : return prev_event->connect_to_next_event_p ();
159 : return false;
160 : }
161 4669 : bool has_out_edge (unsigned range_idx) const final override
162 : {
163 9338 : const event &ev = m_path_label.get_event (range_idx);
164 4669 : return ev.connect_to_next_event_p ();
165 : }
166 :
167 : private:
168 : const path_label &m_path_label;
169 : };
170 :
171 4669 : const event &get_event (unsigned range_idx) const
172 : {
173 4669 : unsigned event_idx = m_start_idx + range_idx;
174 4669 : return m_path.get_event (event_idx);
175 : }
176 :
177 4669 : const event *get_prev_event (unsigned range_idx) const
178 : {
179 4669 : if (m_start_idx + range_idx == 0)
180 : return nullptr;
181 3254 : unsigned event_idx = m_start_idx + range_idx - 1;
182 3254 : return &m_path.get_event (event_idx);
183 : }
184 :
185 : const path &m_path;
186 : const pretty_printer &m_ref_pp;
187 : unsigned m_start_idx;
188 : path_label_effects m_effects;
189 : const bool m_colorize;
190 : const bool m_allow_emojis;
191 : };
192 :
193 : /* Return true if E1 and E2 can be consolidated into the same run of events
194 : when printing a diagnostic path. */
195 :
196 : static bool
197 3568 : can_consolidate_events (const path &p,
198 : const event &e1,
199 : unsigned ev1_idx,
200 : const event &e2,
201 : unsigned ev2_idx,
202 : bool check_locations)
203 : {
204 3568 : if (e1.get_thread_id () != e2.get_thread_id ())
205 : return false;
206 :
207 3565 : if (!p.same_function_p (ev1_idx, ev2_idx))
208 : return false;
209 :
210 3158 : if (e1.get_stack_depth () != e2.get_stack_depth ())
211 : return false;
212 :
213 3126 : if (check_locations)
214 : {
215 3002 : location_t loc1 = e1.get_location ();
216 3002 : location_t loc2 = e2.get_location ();
217 :
218 3002 : if (loc1 < RESERVED_LOCATION_COUNT
219 3002 : || loc2 < RESERVED_LOCATION_COUNT)
220 : return false;
221 :
222 : /* Neither can be macro-based. */
223 2876 : if (linemap_location_from_macro_expansion_p (line_table, loc1))
224 : return false;
225 2842 : if (linemap_location_from_macro_expansion_p (line_table, loc2))
226 : return false;
227 : }
228 :
229 : /* Passed all the tests. */
230 : return true;
231 : }
232 :
233 : struct event_range;
234 : struct path_summary;
235 : class thread_event_printer;
236 :
237 : /* A bundle of information about all of the events in a diagnostic path
238 : relating to a specific path, for use by path_summary. */
239 :
240 : class per_thread_summary
241 : {
242 : public:
243 1508 : per_thread_summary (const path &path_,
244 : const logical_locations::manager &logical_loc_mgr,
245 : label_text name, unsigned swimlane_idx)
246 1508 : : m_path (path_),
247 1508 : m_logical_loc_mgr (logical_loc_mgr),
248 1508 : m_name (std::move (name)),
249 1508 : m_swimlane_idx (swimlane_idx),
250 1508 : m_last_event (nullptr),
251 1508 : m_min_depth (INT_MAX),
252 1508 : m_max_depth (INT_MIN)
253 : {}
254 :
255 5075 : void update_depth_limits (int stack_depth)
256 : {
257 5075 : if (stack_depth < m_min_depth)
258 1512 : m_min_depth = stack_depth;
259 5075 : if (stack_depth > m_max_depth)
260 1715 : m_max_depth = stack_depth;
261 : }
262 :
263 4 : const char *get_name () const { return m_name.get (); }
264 2651 : unsigned get_swimlane_index () const { return m_swimlane_idx; }
265 :
266 : bool interprocedural_p () const;
267 :
268 : private:
269 : friend struct path_summary;
270 : friend class thread_event_printer;
271 : friend struct event_range;
272 :
273 : const path &m_path;
274 : const logical_locations::manager &m_logical_loc_mgr;
275 :
276 : const label_text m_name;
277 :
278 : /* The "swimlane index" is the order in which this per_thread_summary
279 : was created, for use when printing the events. */
280 : const unsigned m_swimlane_idx;
281 :
282 : // The event ranges specific to this thread:
283 : auto_vec<event_range *> m_event_ranges;
284 :
285 : const event *m_last_event;
286 :
287 : int m_min_depth;
288 : int m_max_depth;
289 : };
290 :
291 : /* A stack frame for use in HTML output, holding child stack frames,
292 : and event ranges. */
293 :
294 : struct stack_frame
295 : {
296 7 : stack_frame (std::unique_ptr<stack_frame> parent,
297 : logical_locations::key logical_loc,
298 : int stack_depth)
299 7 : : m_parent (std::move (parent)),
300 7 : m_logical_loc (logical_loc),
301 7 : m_stack_depth (stack_depth)
302 : {}
303 :
304 : std::unique_ptr<stack_frame> m_parent;
305 : logical_locations::key m_logical_loc;
306 : const int m_stack_depth;
307 : };
308 :
309 : /* Begin emitting content relating to a new stack frame within PARENT.
310 : Allocated a new stack_frame and return it. */
311 :
312 : static std::unique_ptr<stack_frame>
313 7 : begin_html_stack_frame (xml::printer &xp,
314 : std::unique_ptr<stack_frame> parent,
315 : logical_locations::key logical_loc,
316 : int stack_depth,
317 : const logical_locations::manager *logical_loc_mgr)
318 : {
319 7 : if (logical_loc)
320 : {
321 6 : gcc_assert (logical_loc_mgr);
322 6 : xp.push_tag_with_class ("table", "stack-frame-with-margin", false);
323 6 : xp.push_tag ("tr", false);
324 6 : {
325 6 : xp.push_tag_with_class ("td", "interprocmargin", false);
326 6 : xp.set_attr ("style", "padding-left: 100px");
327 6 : xp.pop_tag ("td");
328 : }
329 6 : xp.push_tag_with_class ("td", "stack-frame", false);
330 6 : label_text funcname
331 6 : = logical_loc_mgr->get_name_for_path_output (logical_loc);
332 6 : if (funcname.get ())
333 : {
334 6 : xp.push_tag_with_class ("div", "frame-funcname", false);
335 6 : xp.push_tag ("span", true);
336 6 : xp.add_text (funcname.get ());
337 6 : xp.pop_tag ("span");
338 6 : xp.pop_tag ("div");
339 : }
340 6 : }
341 7 : return std::make_unique<stack_frame> (std::move (parent),
342 : logical_loc,
343 7 : stack_depth);
344 : }
345 :
346 : /* Finish emitting content for FRAME and delete it.
347 : Return parent. */
348 :
349 : static std::unique_ptr<stack_frame>
350 7 : end_html_stack_frame (xml::printer &xp,
351 : std::unique_ptr<stack_frame> frame)
352 : {
353 7 : auto parent = std::move (frame->m_parent);
354 7 : if (frame->m_logical_loc)
355 : {
356 6 : xp.pop_tag ("td");
357 6 : xp.pop_tag ("tr");
358 6 : xp.pop_tag ("table");
359 : }
360 7 : return parent;
361 : }
362 :
363 : /* Append an HTML <div> element to XP containing an SVG arrow representing
364 : a change in stack depth from OLD_DEPTH to NEW_DEPTH. */
365 :
366 : static void
367 4 : emit_svg_arrow (xml::printer &xp, int old_depth, int new_depth)
368 : {
369 4 : const int pixels_per_depth = 100;
370 4 : const int min_depth = MIN (old_depth, new_depth);
371 4 : const int base_x = 20;
372 4 : const int excess = 30;
373 4 : const int last_x
374 4 : = base_x + (old_depth - min_depth) * pixels_per_depth;
375 4 : const int this_x
376 4 : = base_x + (new_depth - min_depth) * pixels_per_depth;
377 4 : pretty_printer tmp_pp;
378 4 : pretty_printer *pp = &tmp_pp;
379 5 : pp_printf (pp, "<div class=\"%s\">\n",
380 : old_depth < new_depth
381 : ? "between-ranges-call" : "between-ranges-return");
382 4 : pp_printf (pp, " <svg height=\"30\" width=\"%i\">\n",
383 : MAX (last_x, this_x) + excess);
384 4 : pp_string
385 4 : (pp,
386 : " <defs>\n"
387 : " <marker id=\"arrowhead\" markerWidth=\"10\" markerHeight=\"7\"\n"
388 : " refX=\"0\" refY=\"3.5\" orient=\"auto\" stroke=\"#0088ce\" fill=\"#0088ce\">\n"
389 : " <polygon points=\"0 0, 10 3.5, 0 7\"/>\n"
390 : " </marker>\n"
391 : " </defs>\n");
392 4 : pp_printf (pp,
393 : " <polyline points=\"%i,0 %i,10 %i,10 %i,20\"\n",
394 : last_x, last_x, this_x, this_x);
395 4 : pp_string
396 4 : (pp,
397 : " style=\"fill:none;stroke: #0088ce\"\n"
398 : " marker-end=\"url(#arrowhead)\"/>\n"
399 : " </svg>\n"
400 : "</div>\n\n");
401 4 : xp.add_raw (pp_formatted_text (pp));
402 4 : }
403 :
404 : /* A range of consecutive events within a diagnostic path, all within the
405 : same thread, and with the same fndecl and stack_depth, and which are suitable
406 : to print with a single call to diagnostic_show_locus. */
407 2651 : struct event_range
408 : {
409 : /* A struct for tracking the mergability of labels on a particular
410 : source line. In particular, track information about links between
411 : labels to ensure that we only consolidate events involving links
412 : that the source-printing code is able to handle (splitting them
413 : otherwise). */
414 : struct per_source_line_info
415 : {
416 3805 : void init (int line)
417 : {
418 3805 : m_line = line;
419 3805 : m_has_in_edge = false;
420 3805 : m_has_out_edge = false;
421 3805 : m_min_label_source_column = INT_MAX;
422 3805 : m_max_label_source_column = INT_MIN;
423 3805 : }
424 :
425 : /* Return true if our source-printing/labelling/linking code can handle
426 : the events already on this source line, *and* a new event at COLUMN. */
427 : bool
428 2287 : can_add_label_for_event_p (bool has_in_edge,
429 : const event *prev_event,
430 : bool has_out_edge,
431 : int column) const
432 : {
433 : /* Any existing in-edge has to be the left-most label on its
434 : source line. */
435 2287 : if (m_has_in_edge && column < m_min_label_source_column)
436 : return false;
437 : /* Any existing out-edge has to be the right-most label on its
438 : source line. */
439 2171 : if (m_has_out_edge && column > m_max_label_source_column)
440 : return false;
441 : /* Can't have more than one in-edge. */
442 2051 : if (m_has_in_edge && has_in_edge)
443 : return false;
444 : /* Can't have more than one out-edge. */
445 2051 : if (m_has_out_edge && has_out_edge)
446 : return false;
447 :
448 1995 : if (has_in_edge)
449 : {
450 : /* Any new in-edge needs to be the left-most label on its
451 : source line. */
452 1186 : if (column > m_min_label_source_column)
453 228 : return false;
454 :
455 1186 : gcc_assert (prev_event);
456 1186 : const location_t prev_loc = prev_event->get_location ();
457 1186 : expanded_location prev_exploc
458 : = linemap_client_expand_location_to_spelling_point
459 1186 : (line_table, prev_loc, location_aspect::caret);
460 : /* The destination in-edge's line number has to be <= the
461 : source out-edge's line number (if any). */
462 1186 : if (prev_exploc.line >= m_line)
463 : return false;
464 : }
465 :
466 : /* Any new out-edge needs to be the right-most label on its
467 : source line. */
468 1767 : if (has_out_edge)
469 291 : if (column < m_max_label_source_column)
470 : return false;
471 :
472 : /* All checks passed; we can add the new event at COLUMN. */
473 : return true;
474 : }
475 :
476 : void
477 3806 : add_label_for_event (bool has_in_edge, bool has_out_edge, int column)
478 : {
479 3806 : if (has_in_edge)
480 1486 : m_has_in_edge = true;
481 3806 : if (has_out_edge)
482 1486 : m_has_out_edge = true;
483 3806 : m_min_label_source_column = std::min (m_min_label_source_column, column);
484 3806 : m_max_label_source_column = std::max (m_max_label_source_column, column);
485 : }
486 :
487 : int m_line;
488 : bool m_has_in_edge;
489 : bool m_has_out_edge;
490 : int m_min_label_source_column;
491 : int m_max_label_source_column;
492 : };
493 :
494 2651 : event_range (const path &path_,
495 : const pretty_printer &ref_pp,
496 : unsigned start_idx,
497 : const event &initial_event,
498 : per_thread_summary &t,
499 : bool show_event_links,
500 : bool colorize_labels,
501 : bool allow_emojis)
502 2651 : : m_path (path_),
503 2651 : m_initial_event (initial_event),
504 2651 : m_logical_loc (initial_event.get_logical_location ()),
505 2651 : m_stack_depth (initial_event.get_stack_depth ()),
506 2651 : m_start_idx (start_idx), m_end_idx (start_idx),
507 2651 : m_path_label (path_, ref_pp,
508 : start_idx, colorize_labels, allow_emojis),
509 2651 : m_richloc (initial_event.get_location (), &m_path_label, nullptr),
510 2651 : m_thread_id (initial_event.get_thread_id ()),
511 2651 : m_per_thread_summary (t),
512 2651 : m_show_event_links (show_event_links)
513 : {
514 2651 : if (m_show_event_links)
515 : {
516 2039 : expanded_location exploc
517 : = linemap_client_expand_location_to_spelling_point
518 2039 : (line_table, initial_event.get_location (),
519 : location_aspect::caret);
520 2039 : per_source_line_info &source_line_info
521 2039 : = get_per_source_line_info (exploc.line);
522 :
523 2039 : const event *prev_thread_event = t.m_last_event;
524 2039 : const bool has_in_edge
525 : = (prev_thread_event
526 2039 : ? prev_thread_event->connect_to_next_event_p ()
527 2039 : : false);
528 2039 : const bool has_out_edge = initial_event.connect_to_next_event_p ();
529 :
530 2039 : source_line_info.add_label_for_event
531 4078 : (has_in_edge, has_out_edge, exploc.column);
532 : }
533 2651 : }
534 :
535 : per_source_line_info &
536 4984 : get_per_source_line_info (int source_line)
537 : {
538 4984 : bool existed = false;
539 4984 : per_source_line_info &result
540 4984 : = m_source_line_info_map.get_or_insert (source_line, &existed);
541 4984 : if (!existed)
542 3805 : result.init (source_line);
543 4984 : return result;
544 : }
545 :
546 3568 : bool maybe_add_event (const path_print_policy &policy,
547 : const event &new_ev,
548 : unsigned new_ev_idx,
549 : bool check_rich_locations)
550 : {
551 3568 : if (!can_consolidate_events (m_path,
552 : m_initial_event, m_start_idx,
553 : new_ev, new_ev_idx,
554 : check_rich_locations))
555 : return false;
556 :
557 : /* Verify compatibility of the new label and existing labels
558 : with respect to the link-printing code. */
559 2945 : expanded_location exploc
560 : = linemap_client_expand_location_to_spelling_point
561 2945 : (line_table, new_ev.get_location (), location_aspect::caret);
562 2945 : per_source_line_info &source_line_info
563 2945 : = get_per_source_line_info (exploc.line);
564 2945 : const event *prev_event = nullptr;
565 2945 : if (new_ev_idx > 0)
566 2945 : prev_event = &m_path.get_event (new_ev_idx - 1);
567 2945 : const bool has_in_edge = (prev_event
568 2945 : ? prev_event->connect_to_next_event_p ()
569 2945 : : false);
570 2945 : const bool has_out_edge = new_ev.connect_to_next_event_p ();
571 2945 : if (m_show_event_links)
572 2287 : if (!source_line_info.can_add_label_for_event_p
573 2287 : (has_in_edge, prev_event,
574 : has_out_edge, exploc.column))
575 : return false;
576 :
577 : /* Potentially verify that the locations are sufficiently close. */
578 2425 : if (check_rich_locations)
579 2301 : if (!m_richloc.add_location_if_nearby (policy.get_source_policy (),
580 2301 : new_ev.get_location (),
581 : false, &m_path_label))
582 : return false;
583 :
584 2424 : m_end_idx = new_ev_idx;
585 2424 : m_per_thread_summary.m_last_event = &new_ev;
586 :
587 2424 : if (m_show_event_links)
588 1767 : source_line_info.add_label_for_event
589 3534 : (has_in_edge, has_out_edge, exploc.column);
590 :
591 : return true;
592 : }
593 :
594 : /* Print the events in this range to PP, typically as a single
595 : call to diagnostic_show_locus. */
596 :
597 2644 : void print_as_text (pretty_printer &pp,
598 : diagnostics::text_sink &text_output,
599 : diagnostics::source_effect_info *effect_info)
600 : {
601 2644 : location_t initial_loc = m_initial_event.get_location ();
602 :
603 2644 : diagnostics::context &dc = text_output.get_context ();
604 :
605 : /* Emit a span indicating the filename (and line/column) if the
606 : line has changed relative to the last call to
607 : diagnostic_show_locus. */
608 2644 : if (dc.get_source_printing_options ().enabled)
609 : {
610 2644 : expanded_location exploc
611 : = linemap_client_expand_location_to_spelling_point
612 2644 : (line_table, initial_loc, location_aspect::caret);
613 2644 : if (exploc.file != LOCATION_FILE (dc.m_last_location))
614 : {
615 1330 : diagnostics::location_print_policy loc_policy (text_output);
616 1330 : loc_policy.print_text_span_start (dc, pp, exploc);
617 : }
618 : }
619 :
620 : /* Ideally we will print events as labelled ranges within the
621 : quoted source. But if there is no source, or we can't find it,
622 : we need a fallback, or the events won't show up. Fail more
623 : gracefully in this case by showing the event index and text. */
624 2644 : if (!can_print_source_p (dc))
625 : {
626 686 : for (unsigned i = m_start_idx; i <= m_end_idx; i++)
627 : {
628 405 : const event &iter_event = m_path.get_event (i);
629 405 : location_t event_loc = iter_event.get_location ();
630 405 : if (get_pure_location (event_loc) > BUILTINS_LOCATION)
631 : {
632 : // TODO: this implicitly uses "line_table"
633 0 : gcc_assert (line_table);
634 0 : expanded_location exploc (expand_location (event_loc));
635 :
636 0 : pp_string (&pp, text_output.get_location_text (exploc).get ());
637 : }
638 405 : diagnostic_event_id_t event_id (i);
639 405 : pp_printf (&pp, " %@: ", &event_id);
640 405 : iter_event.print_desc (pp);
641 405 : pp_newline (&pp);
642 : }
643 : return;
644 : }
645 :
646 : /* Call diagnostic_show_locus to show the events using labels. */
647 2363 : diagnostic_show_locus (&dc, text_output.get_source_printing_options (),
648 : &m_richloc, diagnostics::kind::path, &pp,
649 : effect_info);
650 :
651 : /* If we have a macro expansion, show the expansion to the user. */
652 2363 : if (linemap_location_from_macro_expansion_p (line_table, initial_loc))
653 : {
654 44 : gcc_assert (m_start_idx == m_end_idx);
655 44 : maybe_unwind_expanded_macro_loc (text_output, initial_loc);
656 : }
657 : }
658 :
659 : /* Print the events in this range to XP, typically as a single
660 : call to diagnostic_show_locus_as_html. */
661 :
662 7 : void print_as_html (xml::printer &xp,
663 : diagnostics::context &dc,
664 : diagnostics::source_effect_info *effect_info,
665 : html_label_writer *event_label_writer)
666 : {
667 7 : location_t initial_loc = m_initial_event.get_location ();
668 :
669 : /* Emit a span indicating the filename (and line/column) if the
670 : line has changed relative to the last call to
671 : diagnostic_show_locus. */
672 7 : if (dc.get_source_printing_options ().enabled)
673 : {
674 7 : expanded_location exploc
675 : = linemap_client_expand_location_to_spelling_point
676 7 : (line_table, initial_loc, location_aspect::caret);
677 7 : if (exploc.file != LOCATION_FILE (dc.m_last_location))
678 : {
679 1 : diagnostics::location_print_policy loc_policy (dc);
680 1 : loc_policy.print_html_span_start (dc, xp, exploc);
681 : }
682 : }
683 :
684 : /* Ideally we will print events as labelled ranges within the
685 : quoted source. But if there is no source, or we can't find it,
686 : we need a fallback, or the events won't show up. Fail more
687 : gracefully in this case by showing the event index and text. */
688 7 : if (!can_print_source_p (dc))
689 : {
690 2 : for (unsigned i = m_start_idx; i <= m_end_idx; i++)
691 : {
692 1 : const event &iter_event = m_path.get_event (i);
693 :
694 1 : xml::auto_print_element p (xp, "p");
695 :
696 1 : if (event_label_writer)
697 1 : event_label_writer->begin_label ();
698 :
699 1 : location_t event_loc = iter_event.get_location ();
700 1 : if (get_pure_location (event_loc) > BUILTINS_LOCATION)
701 : {
702 : // TODO: this implicitly uses "line_table"
703 0 : gcc_assert (line_table);
704 0 : expanded_location exploc (expand_location (event_loc));
705 :
706 0 : location_print_policy policy (dc);
707 0 : policy.print_html_span_start (dc, xp, exploc);
708 : }
709 1 : {
710 1 : diagnostic_event_id_t event_id (i);
711 1 : pretty_printer pp;
712 1 : pp_printf (&pp, " %@: ", &event_id);
713 1 : xp.push_tag_with_class ("span", "event-id");
714 1 : xp.add_text_from_pp (pp);
715 1 : xp.pop_tag ("span");
716 1 : }
717 1 : {
718 1 : pretty_printer pp;
719 1 : iter_event.print_desc (pp);
720 1 : xp.push_tag_with_class ("span", "event-text");
721 1 : xp.add_text_from_pp (pp);
722 1 : xp.pop_tag ("span");
723 1 : }
724 1 : if (event_label_writer)
725 1 : event_label_writer->end_label ();
726 1 : }
727 : return;
728 : }
729 :
730 : /* Call diagnostic_show_locus_as_html to show the source,
731 : showing events using labels. */
732 6 : diagnostic_show_locus_as_html (&dc, dc.get_source_printing_options (),
733 : &m_richloc, diagnostics::kind::path, xp,
734 : effect_info, event_label_writer);
735 :
736 : // TODO: show macro expansions
737 : }
738 :
739 : const path &m_path;
740 : const event &m_initial_event;
741 : logical_locations::key m_logical_loc;
742 : int m_stack_depth;
743 : unsigned m_start_idx;
744 : unsigned m_end_idx;
745 : path_label m_path_label;
746 : gcc_rich_location m_richloc;
747 : thread_id_t m_thread_id;
748 : per_thread_summary &m_per_thread_summary;
749 : hash_map<int_hash<int, -1, -2>,
750 : per_source_line_info> m_source_line_info_map;
751 : bool m_show_event_links;
752 :
753 : private:
754 : /* Return true if we can print source code for the primary location
755 : for the initial event.
756 :
757 : If we can't then the labels won't be printed, and thus we'll have
758 : to fall back to printing the events directly for them to be
759 : printed. */
760 2651 : bool can_print_source_p (diagnostics::context &dc) const
761 : {
762 2651 : location_t initial_loc = m_initial_event.get_location ();
763 2651 : if (get_pure_location (initial_loc) <= BUILTINS_LOCATION)
764 : return false;
765 :
766 : // TODO: this implicitly uses "line_table"
767 2369 : expanded_location exploc (expand_location (initial_loc));
768 :
769 2369 : auto line_content
770 2369 : = dc.get_file_cache ().get_source_line (exploc.file, exploc.line);
771 2369 : if (!line_content)
772 : return false;
773 :
774 : return true;
775 : }
776 : };
777 :
778 : /* A struct for grouping together the events in a path into
779 : ranges of events, partitioned by thread and by stack frame (i.e. by fndecl
780 : and stack depth). */
781 :
782 : struct path_summary
783 : {
784 : path_summary (const path_print_policy &policy,
785 : const pretty_printer &ref_pp,
786 : const path &path_,
787 : bool check_rich_locations,
788 : bool colorize = false,
789 : bool show_event_links = true);
790 :
791 2644 : const logical_locations::manager &get_logical_location_manager () const
792 : {
793 2644 : return m_logical_loc_mgr;
794 : }
795 40 : unsigned get_num_ranges () const { return m_ranges.length (); }
796 7932 : bool multithreaded_p () const { return m_per_thread_summary.length () > 1; }
797 :
798 : const per_thread_summary &get_events_for_thread_id (thread_id_t tid)
799 : {
800 : per_thread_summary **slot = m_thread_id_to_events.get (tid);
801 : gcc_assert (slot);
802 : gcc_assert (*slot);
803 : return **slot;
804 : }
805 :
806 : const logical_locations::manager &m_logical_loc_mgr;
807 : auto_delete_vec <event_range> m_ranges;
808 : auto_delete_vec <per_thread_summary> m_per_thread_summary;
809 : hash_map<int_hash<thread_id_t, -1, -2>,
810 : per_thread_summary *> m_thread_id_to_events;
811 :
812 : private:
813 : per_thread_summary &
814 5075 : get_or_create_events_for_thread_id (const path &path_,
815 : thread_id_t tid)
816 : {
817 5075 : if (per_thread_summary **slot = m_thread_id_to_events.get (tid))
818 3567 : return **slot;
819 :
820 1508 : const thread &thread = path_.get_thread (tid);
821 1508 : per_thread_summary *pts
822 : = new per_thread_summary (path_,
823 : m_logical_loc_mgr,
824 3016 : thread.get_name (false),
825 1509 : m_per_thread_summary.length ());
826 1508 : m_thread_id_to_events.put (tid, pts);
827 1508 : m_per_thread_summary.safe_push (pts);
828 1508 : return *pts;
829 : }
830 : };
831 :
832 : /* Return true iff there is more than one stack frame used by the events
833 : of this thread. */
834 :
835 : bool
836 2644 : per_thread_summary::interprocedural_p () const
837 : {
838 4677 : if (m_event_ranges.is_empty ())
839 : return false;
840 2644 : int first_stack_depth = m_event_ranges[0]->m_stack_depth;
841 6962 : for (auto range : m_event_ranges)
842 : {
843 4929 : if (!m_path.same_function_p (m_event_ranges[0]->m_start_idx,
844 4929 : range->m_start_idx))
845 : return true;
846 4373 : if (range->m_stack_depth != first_stack_depth)
847 : return true;
848 : }
849 : return false;
850 : }
851 :
852 : /* path_summary's ctor. */
853 :
854 1516 : path_summary::path_summary (const path_print_policy &policy,
855 : const pretty_printer &ref_pp,
856 : const path &path_,
857 : bool check_rich_locations,
858 : bool colorize,
859 1516 : bool show_event_links)
860 1516 : : m_logical_loc_mgr (path_.get_logical_location_manager ())
861 : {
862 1516 : const unsigned num_events = path_.num_events ();
863 :
864 1516 : event_range *cur_event_range = nullptr;
865 6591 : for (unsigned idx = 0; idx < num_events; idx++)
866 : {
867 5075 : const event &ev = path_.get_event (idx);
868 5075 : const thread_id_t thread_id = ev.get_thread_id ();
869 5075 : per_thread_summary &pts
870 5075 : = get_or_create_events_for_thread_id (path_, thread_id);
871 :
872 5075 : pts.update_depth_limits (ev.get_stack_depth ());
873 :
874 5075 : if (cur_event_range)
875 3568 : if (cur_event_range->maybe_add_event (policy,
876 : ev,
877 : idx, check_rich_locations))
878 2424 : continue;
879 :
880 2651 : auto theme = policy.get_diagram_theme ();
881 2651 : const bool allow_emojis = theme ? theme->emojis_p () : false;
882 5302 : cur_event_range = new event_range (path_, ref_pp,
883 : idx, ev, pts,
884 : show_event_links,
885 : colorize,
886 2651 : allow_emojis);
887 2651 : m_ranges.safe_push (cur_event_range);
888 2651 : pts.m_event_ranges.safe_push (cur_event_range);
889 2651 : pts.m_last_event = &ev;
890 : }
891 1516 : }
892 :
893 : /* Write SPACES to PP. */
894 :
895 : static void
896 4697 : write_indent (pretty_printer *pp, int spaces)
897 : {
898 32127 : for (int i = 0; i < spaces; i++)
899 27430 : pp_space (pp);
900 0 : }
901 :
902 : static const int base_indent = 2;
903 : static const int per_frame_indent = 2;
904 :
905 : /* A bundle of state for printing event_range instances for a particular
906 : thread. */
907 :
908 1509 : class thread_event_printer
909 : {
910 : public:
911 1508 : thread_event_printer (const per_thread_summary &t, bool show_depths)
912 1508 : : m_per_thread_summary (t),
913 1508 : m_show_depths (show_depths),
914 1508 : m_cur_indent (base_indent),
915 1508 : m_vbar_column_for_depth (),
916 1508 : m_num_printed (0)
917 : {
918 1508 : }
919 :
920 : /* Get the previous event_range within this thread, if any. */
921 2644 : const event_range *get_any_prev_range () const
922 : {
923 2644 : if (m_num_printed > 0)
924 1139 : return m_per_thread_summary.m_event_ranges[m_num_printed - 1];
925 : else
926 : return nullptr;
927 : }
928 :
929 : /* Get the next event_range within this thread, if any. */
930 2644 : const event_range *get_any_next_range () const
931 : {
932 5288 : if (m_num_printed < m_per_thread_summary.m_event_ranges.length () - 1)
933 1139 : return m_per_thread_summary.m_event_ranges[m_num_printed + 1];
934 : else
935 : return nullptr;
936 : }
937 :
938 : void
939 2644 : print_swimlane_for_event_range_as_text (diagnostics::text_sink &text_output,
940 : pretty_printer *pp,
941 : const logical_locations::manager &logical_loc_mgr,
942 : event_range *range,
943 : diagnostics::source_effect_info *effect_info)
944 : {
945 2644 : gcc_assert (pp);
946 2644 : const char *const line_color = "path";
947 2644 : const char *start_line_color
948 2644 : = colorize_start (pp_show_color (pp), line_color);
949 2644 : const char *end_line_color = colorize_stop (pp_show_color (pp));
950 :
951 2644 : text_art::ascii_theme fallback_theme;
952 2644 : text_art::theme *theme = text_output.get_diagram_theme ();
953 2644 : if (!theme)
954 588 : theme = &fallback_theme;
955 :
956 2644 : cppchar_t depth_marker_char = theme->get_cppchar
957 2644 : (text_art::theme::cell_kind::INTERPROCEDURAL_DEPTH_MARKER);
958 : /* e.g. "|". */
959 :
960 2644 : const bool interprocedural_p = m_per_thread_summary.interprocedural_p ();
961 :
962 2644 : write_indent (pp, m_cur_indent);
963 2644 : if (const event_range *prev_range = get_any_prev_range ())
964 : {
965 1139 : if (range->m_stack_depth > prev_range->m_stack_depth)
966 : {
967 322 : gcc_assert (interprocedural_p);
968 : /* Show pushed stack frame(s). */
969 322 : cppchar_t left = theme->get_cppchar
970 322 : (text_art::theme::cell_kind::INTERPROCEDURAL_PUSH_FRAME_LEFT);
971 322 : cppchar_t middle = theme->get_cppchar
972 322 : (text_art::theme::cell_kind::INTERPROCEDURAL_PUSH_FRAME_MIDDLE);
973 322 : cppchar_t right = theme->get_cppchar
974 322 : (text_art::theme::cell_kind::INTERPROCEDURAL_PUSH_FRAME_RIGHT);
975 : /* e.g. "+--> ". */
976 322 : pp_string (pp, start_line_color);
977 322 : pp_unicode_character (pp, left);
978 322 : pp_unicode_character (pp, middle);
979 322 : pp_unicode_character (pp, middle);
980 322 : pp_unicode_character (pp, right);
981 322 : pp_space (pp);
982 322 : pp_string (pp, end_line_color);
983 322 : m_cur_indent += 5;
984 : }
985 : }
986 2644 : if (range->m_logical_loc)
987 : {
988 745 : label_text name
989 745 : (logical_loc_mgr.get_name_for_path_output (range->m_logical_loc));
990 745 : if (name.get ())
991 745 : pp_printf (pp, "%qs: ", name.get ());
992 745 : }
993 2644 : if (range->m_start_idx == range->m_end_idx)
994 1103 : pp_printf (pp, "event %i",
995 : range->m_start_idx + 1);
996 : else
997 1541 : pp_printf (pp, "events %i-%i",
998 : range->m_start_idx + 1, range->m_end_idx + 1);
999 2644 : if (m_show_depths)
1000 325 : pp_printf (pp, " (depth %i)", range->m_stack_depth);
1001 2644 : pp_newline (pp);
1002 :
1003 : /* Print a run of events. */
1004 2644 : if (interprocedural_p)
1005 : {
1006 611 : write_indent (pp, m_cur_indent + per_frame_indent);
1007 611 : pp_string (pp, start_line_color);
1008 611 : pp_unicode_character (pp, depth_marker_char);
1009 611 : pp_string (pp, end_line_color);
1010 611 : pp_newline (pp);
1011 :
1012 611 : char *saved_prefix = pp_take_prefix (pp);
1013 611 : char *prefix;
1014 611 : {
1015 611 : pretty_printer tmp_pp;
1016 611 : write_indent (&tmp_pp, m_cur_indent + per_frame_indent);
1017 611 : pp_string (&tmp_pp, start_line_color);
1018 611 : pp_unicode_character (&tmp_pp, depth_marker_char);
1019 611 : pp_string (&tmp_pp, end_line_color);
1020 611 : prefix = xstrdup (pp_formatted_text (&tmp_pp));
1021 611 : }
1022 611 : pp_set_prefix (pp, prefix);
1023 611 : pp_prefixing_rule (pp) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
1024 611 : range->print_as_text (*pp, text_output, effect_info);
1025 611 : pp_set_prefix (pp, saved_prefix);
1026 :
1027 611 : write_indent (pp, m_cur_indent + per_frame_indent);
1028 611 : pp_string (pp, start_line_color);
1029 611 : pp_unicode_character (pp, depth_marker_char);
1030 611 : pp_string (pp, end_line_color);
1031 611 : pp_newline (pp);
1032 : }
1033 : else
1034 2033 : range->print_as_text (*pp, text_output, effect_info);
1035 :
1036 2644 : if (const event_range *next_range = get_any_next_range ())
1037 : {
1038 1139 : if (range->m_stack_depth > next_range->m_stack_depth)
1039 : {
1040 113 : if (m_vbar_column_for_depth.get (next_range->m_stack_depth))
1041 : {
1042 : /* Show returning from stack frame(s), by printing
1043 : something like:
1044 : " |\n"
1045 : " <-------------+\n"
1046 : " |\n". */
1047 110 : gcc_assert (interprocedural_p);
1048 110 : cppchar_t left = theme->get_cppchar
1049 110 : (text_art::theme::cell_kind::INTERPROCEDURAL_POP_FRAMES_LEFT);
1050 110 : cppchar_t middle = theme->get_cppchar
1051 110 : (text_art::theme::cell_kind::INTERPROCEDURAL_POP_FRAMES_MIDDLE);
1052 110 : cppchar_t right = theme->get_cppchar
1053 110 : (text_art::theme::cell_kind::INTERPROCEDURAL_POP_FRAMES_RIGHT);
1054 110 : int vbar_for_next_frame
1055 110 : = *m_vbar_column_for_depth.get (next_range->m_stack_depth);
1056 :
1057 110 : int indent_for_next_frame
1058 : = vbar_for_next_frame - per_frame_indent;
1059 110 : write_indent (pp, vbar_for_next_frame);
1060 110 : pp_string (pp, start_line_color);
1061 110 : pp_unicode_character (pp, left);
1062 1022 : for (int i = indent_for_next_frame + per_frame_indent;
1063 1022 : i < m_cur_indent + per_frame_indent - 1; i++)
1064 912 : pp_unicode_character (pp, middle);
1065 110 : pp_unicode_character (pp, right);
1066 110 : pp_string (pp, end_line_color);
1067 110 : pp_newline (pp);
1068 110 : m_cur_indent = indent_for_next_frame;
1069 :
1070 110 : write_indent (pp, vbar_for_next_frame);
1071 110 : pp_string (pp, start_line_color);
1072 110 : pp_unicode_character (pp, depth_marker_char);
1073 110 : pp_string (pp, end_line_color);
1074 110 : pp_newline (pp);
1075 : }
1076 : else
1077 : {
1078 : /* Handle disjoint paths (e.g. a callback at some later
1079 : time). */
1080 3 : m_cur_indent = base_indent;
1081 : }
1082 : }
1083 1026 : else if (range->m_stack_depth < next_range->m_stack_depth)
1084 : {
1085 : /* Prepare to show pushed stack frame. */
1086 322 : gcc_assert (interprocedural_p);
1087 322 : gcc_assert (range->m_stack_depth != EMPTY);
1088 322 : gcc_assert (range->m_stack_depth != DELETED);
1089 322 : m_vbar_column_for_depth.put (range->m_stack_depth,
1090 322 : m_cur_indent + per_frame_indent);
1091 322 : m_cur_indent += per_frame_indent;
1092 : }
1093 : }
1094 :
1095 2644 : m_num_printed++;
1096 2644 : }
1097 :
1098 : void
1099 7 : print_swimlane_for_event_range_as_html (diagnostics::context &dc,
1100 : xml::printer &xp,
1101 : html_label_writer *event_label_writer,
1102 : event_range *range,
1103 : diagnostics::source_effect_info *effect_info)
1104 : {
1105 7 : range->print_as_html (xp, dc, effect_info, event_label_writer);
1106 7 : m_num_printed++;
1107 : }
1108 :
1109 : int get_cur_indent () const { return m_cur_indent; }
1110 :
1111 : private:
1112 : const per_thread_summary &m_per_thread_summary;
1113 : bool m_show_depths;
1114 :
1115 : /* Print the ranges. */
1116 : int m_cur_indent;
1117 :
1118 : /* Keep track of column numbers of existing '|' characters for
1119 : stack depths we've already printed. */
1120 : static const int EMPTY = -1;
1121 : static const int DELETED = -2;
1122 : typedef int_hash <int, EMPTY, DELETED> vbar_hash;
1123 : hash_map <vbar_hash, int> m_vbar_column_for_depth;
1124 :
1125 : /* How many event ranges within this swimlane have we printed.
1126 : This is the index of the next event_range to print. */
1127 : unsigned m_num_printed;
1128 : };
1129 :
1130 : /* Print path_summary PS to TEXT_OUTPUT, giving an overview of the
1131 : interprocedural calls and returns.
1132 :
1133 : Print the event descriptions in a nested form, printing the event
1134 : descriptions within calls to diagnostic_show_locus, using labels to
1135 : show the events:
1136 :
1137 : 'foo' (events 1-2)
1138 : | NN |
1139 : | |
1140 : +--> 'bar' (events 3-4)
1141 : | NN |
1142 : | |
1143 : +--> 'baz' (events 5-6)
1144 : | NN |
1145 : | |
1146 : <------------ +
1147 : |
1148 : 'foo' (events 7-8)
1149 : | NN |
1150 : | |
1151 : +--> 'bar' (events 9-10)
1152 : | NN |
1153 : | |
1154 : +--> 'baz' (events 11-12)
1155 : | NN |
1156 : | |
1157 :
1158 : If SHOW_DEPTHS is true, append " (depth N)" to the header of each run
1159 : of events.
1160 :
1161 : For events with UNKNOWN_LOCATION, print a summary of each the event. */
1162 :
1163 : static void
1164 1513 : print_path_summary_as_text (const path_summary &ps,
1165 : diagnostics::text_sink &text_output,
1166 : bool show_depths)
1167 : {
1168 1513 : pretty_printer *const pp = text_output.get_printer ();
1169 :
1170 1513 : std::vector<thread_event_printer> thread_event_printers;
1171 6026 : for (auto t : ps.m_per_thread_summary)
1172 1505 : thread_event_printers.push_back (thread_event_printer (*t, show_depths));
1173 :
1174 : unsigned i;
1175 : event_range *range;
1176 : int last_out_edge_column = -1;
1177 4157 : FOR_EACH_VEC_ELT (ps.m_ranges, i, range)
1178 : {
1179 2644 : const int swimlane_idx
1180 2644 : = range->m_per_thread_summary.get_swimlane_index ();
1181 2644 : if (ps.multithreaded_p ())
1182 6 : if (i == 0 || ps.m_ranges[i - 1]->m_thread_id != range->m_thread_id)
1183 : {
1184 4 : if (i > 0)
1185 3 : pp_newline (pp);
1186 4 : pp_printf (pp, "Thread: %qs",
1187 4 : range->m_per_thread_summary.get_name ());
1188 4 : pp_newline (pp);
1189 : }
1190 2644 : thread_event_printer &tep = thread_event_printers[swimlane_idx];
1191 : /* Wire up any trailing out-edge from previous range to leading in-edge
1192 : of this range. */
1193 2644 : diagnostics::source_effect_info effect_info;
1194 2644 : effect_info.m_leading_in_edge_column = last_out_edge_column;
1195 2644 : tep.print_swimlane_for_event_range_as_text
1196 2644 : (text_output, pp,
1197 : ps.get_logical_location_manager (),
1198 : range, &effect_info);
1199 2644 : last_out_edge_column = effect_info.m_trailing_out_edge_column;
1200 : }
1201 1513 : }
1202 :
1203 : /* Print PS as HTML to XP, using DC and, if non-null EVENT_LABEL_WRITER. */
1204 :
1205 : static void
1206 3 : print_path_summary_as_html (const path_summary &ps,
1207 : diagnostics::context &dc,
1208 : xml::printer &xp,
1209 : html_label_writer *event_label_writer,
1210 : bool show_depths)
1211 : {
1212 3 : std::vector<thread_event_printer> thread_event_printers;
1213 12 : for (auto t : ps.m_per_thread_summary)
1214 3 : thread_event_printers.push_back (thread_event_printer (*t, show_depths));
1215 :
1216 3 : const logical_locations::manager *logical_loc_mgr
1217 3 : = dc.get_logical_location_manager ();
1218 :
1219 3 : xp.push_tag_with_class ("div", "event-ranges", false);
1220 :
1221 : /* Group the ranges into stack frames. */
1222 3 : std::unique_ptr<stack_frame> curr_frame;
1223 3 : unsigned i;
1224 3 : event_range *range;
1225 3 : int last_out_edge_column = -1;
1226 10 : FOR_EACH_VEC_ELT (ps.m_ranges, i, range)
1227 : {
1228 7 : const int swimlane_idx
1229 7 : = range->m_per_thread_summary.get_swimlane_index ();
1230 :
1231 7 : const logical_locations::key this_logical_loc = range->m_logical_loc;
1232 7 : const int this_depth = range->m_stack_depth;
1233 7 : if (curr_frame)
1234 : {
1235 4 : int old_stack_depth = curr_frame->m_stack_depth;
1236 4 : if (this_depth > curr_frame->m_stack_depth)
1237 : {
1238 3 : emit_svg_arrow (xp, old_stack_depth, this_depth);
1239 3 : curr_frame
1240 6 : = begin_html_stack_frame (xp,
1241 : std::move (curr_frame),
1242 : range->m_logical_loc,
1243 : range->m_stack_depth,
1244 3 : logical_loc_mgr);
1245 : }
1246 : else
1247 : {
1248 2 : while (this_depth < curr_frame->m_stack_depth
1249 2 : || this_logical_loc != curr_frame->m_logical_loc)
1250 : {
1251 2 : curr_frame = end_html_stack_frame (xp, std::move (curr_frame));
1252 2 : if (curr_frame == nullptr)
1253 : {
1254 1 : curr_frame
1255 1 : = begin_html_stack_frame (xp,
1256 2 : nullptr,
1257 : range->m_logical_loc,
1258 : range->m_stack_depth,
1259 1 : logical_loc_mgr);
1260 1 : break;
1261 : }
1262 : }
1263 1 : emit_svg_arrow (xp, old_stack_depth, this_depth);
1264 : }
1265 : }
1266 : else
1267 : {
1268 3 : curr_frame = begin_html_stack_frame (xp,
1269 6 : nullptr,
1270 : range->m_logical_loc,
1271 : range->m_stack_depth,
1272 3 : logical_loc_mgr);
1273 : }
1274 :
1275 7 : xp.push_tag_with_class ("table", "event-range-with-margin", false);
1276 7 : xp.push_tag ("tr", false);
1277 7 : xp.push_tag_with_class ("td", "event-range", false);
1278 7 : xp.push_tag_with_class ("div", "events-hdr", true);
1279 7 : if (range->m_logical_loc)
1280 : {
1281 6 : gcc_assert (logical_loc_mgr);
1282 6 : label_text funcname
1283 6 : = logical_loc_mgr->get_name_for_path_output (range->m_logical_loc);
1284 6 : if (funcname.get ())
1285 : {
1286 6 : xp.push_tag_with_class ("span", "funcname", true);
1287 6 : xp.add_text (funcname.get ());
1288 6 : xp.pop_tag ("span");
1289 6 : xp.add_text (": ");
1290 : }
1291 6 : }
1292 7 : {
1293 7 : xp.push_tag_with_class ("span", "event-ids", true);
1294 7 : pretty_printer pp;
1295 7 : if (range->m_start_idx == range->m_end_idx)
1296 2 : pp_printf (&pp, "event %i",
1297 : range->m_start_idx + 1);
1298 : else
1299 5 : pp_printf (&pp, "events %i-%i",
1300 : range->m_start_idx + 1, range->m_end_idx + 1);
1301 7 : xp.add_text_from_pp (pp);
1302 7 : xp.pop_tag ("span");
1303 7 : }
1304 7 : if (show_depths)
1305 : {
1306 0 : xp.add_text (" ");
1307 0 : xp.push_tag_with_class ("span", "depth", true);
1308 0 : pretty_printer pp;
1309 0 : pp_printf (&pp, "(depth %i)", range->m_stack_depth);
1310 0 : xp.add_text_from_pp (pp);
1311 0 : xp.pop_tag ("span");
1312 0 : }
1313 7 : xp.pop_tag ("div");
1314 :
1315 : /* Print a run of events. */
1316 7 : thread_event_printer &tep = thread_event_printers[swimlane_idx];
1317 : /* Wire up any trailing out-edge from previous range to leading in-edge
1318 : of this range. */
1319 7 : diagnostics::source_effect_info effect_info;
1320 7 : effect_info.m_leading_in_edge_column = last_out_edge_column;
1321 7 : tep.print_swimlane_for_event_range_as_html (dc, xp, event_label_writer,
1322 : range, &effect_info);
1323 7 : last_out_edge_column = effect_info.m_trailing_out_edge_column;
1324 :
1325 7 : xp.pop_tag ("td");
1326 7 : xp.pop_tag ("tr");
1327 7 : xp.pop_tag ("table");
1328 : }
1329 :
1330 : /* Close outstanding frames. */
1331 8 : while (curr_frame)
1332 5 : curr_frame = end_html_stack_frame (xp, std::move (curr_frame));
1333 :
1334 3 : xp.pop_tag ("div");
1335 3 : }
1336 :
1337 : } /* end of anonymous namespace for path-printing code. */
1338 :
1339 14820 : class element_event_desc : public pp_element
1340 : {
1341 : public:
1342 14820 : element_event_desc (const event &event_)
1343 14820 : : m_event (event_)
1344 : {
1345 : }
1346 :
1347 14911 : void add_to_phase_2 (pp_markup::context &ctxt) final override
1348 : {
1349 14911 : auto pp = ctxt.m_pp.clone ();
1350 14911 : m_event.print_desc (*pp.get ());
1351 14911 : pp_string (&ctxt.m_pp, pp_formatted_text (pp.get ()));
1352 14911 : }
1353 :
1354 : private:
1355 : const event &m_event;
1356 : };
1357 :
1358 : /* Print PATH according to the context's path_format. */
1359 :
1360 : void
1361 9675 : diagnostics::text_sink::print_path (const path &path_)
1362 : {
1363 9675 : const unsigned num_events = path_.num_events ();
1364 :
1365 9675 : switch (get_context ().get_path_format ())
1366 : {
1367 : case DPF_NONE:
1368 : /* Do nothing. */
1369 : return;
1370 :
1371 9437 : case DPF_SEPARATE_EVENTS:
1372 9437 : {
1373 : /* A note per event. */
1374 9437 : auto &logical_loc_mgr = path_.get_logical_location_manager ();
1375 24257 : for (unsigned i = 0; i < num_events; i++)
1376 : {
1377 14820 : const event &ev = path_.get_event (i);
1378 14820 : element_event_desc e_event_desc (ev);
1379 14820 : diagnostic_event_id_t event_id (i);
1380 14820 : if (get_context ().show_path_depths_p ())
1381 : {
1382 172 : int stack_depth = ev.get_stack_depth ();
1383 : /* -fdiagnostics-path-format=separate-events doesn't print
1384 : fndecl information, so with -fdiagnostics-show-path-depths
1385 : print the fndecls too, if any. */
1386 172 : if (logical_locations::key logical_loc
1387 172 : = ev.get_logical_location ())
1388 : {
1389 172 : label_text name
1390 172 : (logical_loc_mgr.get_name_for_path_output (logical_loc));
1391 172 : inform (ev.get_location (),
1392 : "%@ %e (fndecl %qs, depth %i)",
1393 : &event_id, &e_event_desc,
1394 : name.get (), stack_depth);
1395 172 : }
1396 : else
1397 0 : inform (ev.get_location (),
1398 : "%@ %e (depth %i)",
1399 : &event_id, &e_event_desc,
1400 : stack_depth);
1401 : }
1402 : else
1403 14648 : inform (ev.get_location (),
1404 : "%@ %e", &event_id, &e_event_desc);
1405 14820 : }
1406 : }
1407 : break;
1408 :
1409 229 : case DPF_INLINE_EVENTS:
1410 229 : {
1411 : /* Consolidate related events. */
1412 229 : path_print_policy policy (*this);
1413 229 : pretty_printer *const pp = get_printer ();
1414 229 : const bool check_rich_locations = true;
1415 229 : const bool colorize = pp_show_color (pp);
1416 229 : const bool show_event_links = m_source_printing.show_event_links_p;
1417 229 : path_summary summary (policy,
1418 : *pp,
1419 : path_,
1420 : check_rich_locations,
1421 : colorize,
1422 229 : show_event_links);
1423 229 : char *saved_prefix = pp_take_prefix (pp);
1424 229 : pp_set_prefix (pp, nullptr);
1425 229 : print_path_summary_as_text (summary, *this,
1426 229 : get_context ().show_path_depths_p ());
1427 229 : pp_flush (pp);
1428 229 : pp_set_prefix (pp, saved_prefix);
1429 229 : }
1430 229 : break;
1431 : }
1432 : }
1433 :
1434 : /* Print PATH_ as HTML to XP, using DC and DSPP for settings.
1435 : If non-null, use EVENT_LABEL_WRITER when writing events. */
1436 :
1437 : void
1438 3 : diagnostics::print_path_as_html (xml::printer &xp,
1439 : const path &path_,
1440 : context &dc,
1441 : html_label_writer *event_label_writer,
1442 : const source_print_policy &dspp)
1443 : {
1444 3 : path_print_policy policy (dc);
1445 3 : const bool check_rich_locations = true;
1446 3 : const bool colorize = false;
1447 3 : const source_printing_options &source_printing_opts
1448 3 : = dspp.get_options ();
1449 3 : const bool show_event_links = source_printing_opts.show_event_links_p;
1450 3 : path_summary summary (policy,
1451 3 : *dc.get_reference_printer (),
1452 : path_,
1453 : check_rich_locations,
1454 : colorize,
1455 3 : show_event_links);
1456 3 : print_path_summary_as_html (summary, dc, xp, event_label_writer,
1457 3 : dc.show_path_depths_p ());
1458 3 : }
1459 :
1460 : #if CHECKING_P
1461 :
1462 : namespace diagnostics {
1463 : namespace paths {
1464 : namespace selftest {
1465 :
1466 : using location = ::selftest::location;
1467 : using line_table_case = ::selftest::line_table_case;
1468 : using line_table_test = ::selftest::line_table_test;
1469 : using temp_source_file = ::selftest::temp_source_file;
1470 :
1471 : using test_context = diagnostics::selftest::test_context;
1472 :
1473 : /* Return true iff all events in PATH_ have locations for which column data
1474 : is available, so that selftests that require precise string output can
1475 : bail out for awkward line_table cases. */
1476 :
1477 : static bool
1478 1632 : path_events_have_column_data_p (const path &path_)
1479 : {
1480 4388 : for (unsigned idx = 0; idx < path_.num_events (); idx++)
1481 : {
1482 3416 : location_t event_loc = path_.get_event (idx).get_location ();
1483 3416 : if (line_table->get_pure_location (event_loc)
1484 : > LINE_MAP_MAX_LOCATION_WITH_COLS)
1485 : return false;
1486 2756 : if (line_table->get_start (event_loc) > LINE_MAP_MAX_LOCATION_WITH_COLS)
1487 : return false;
1488 2756 : if (line_table->get_finish (event_loc) > LINE_MAP_MAX_LOCATION_WITH_COLS)
1489 : return false;
1490 : }
1491 : return true;
1492 : }
1493 :
1494 : /* Verify that empty paths are handled gracefully. */
1495 :
1496 : static void
1497 4 : test_empty_path (pretty_printer *event_pp)
1498 : {
1499 4 : logical_locations::selftest::test_manager logical_loc_mgr;
1500 4 : test_path path (logical_loc_mgr, event_pp);
1501 4 : ASSERT_FALSE (path.interprocedural_p ());
1502 :
1503 4 : test_context dc;
1504 4 : text_sink text_output (dc);
1505 4 : path_print_policy policy (text_output);
1506 4 : path_summary summary (policy, *event_pp, path, false);
1507 4 : ASSERT_EQ (summary.get_num_ranges (), 0);
1508 :
1509 4 : print_path_summary_as_text (summary, text_output, true);
1510 4 : ASSERT_STREQ ("",
1511 : pp_formatted_text (text_output.get_printer ()));
1512 4 : }
1513 :
1514 : /* Verify that print_path_summary works on a purely intraprocedural path. */
1515 :
1516 : static void
1517 4 : test_intraprocedural_path (pretty_printer *event_pp)
1518 : {
1519 4 : logical_locations::selftest::test_manager logical_loc_mgr;
1520 4 : test_path path (logical_loc_mgr, event_pp);
1521 4 : const char *const funcname = "foo";
1522 4 : path.add_event (UNKNOWN_LOCATION, funcname, 0, "first %qs", "free");
1523 4 : path.add_event (UNKNOWN_LOCATION, funcname, 0, "double %qs", "free");
1524 :
1525 4 : ASSERT_FALSE (path.interprocedural_p ());
1526 :
1527 4 : selftest::test_context dc;
1528 4 : text_sink text_output (dc);
1529 4 : path_print_policy policy (text_output);
1530 4 : path_summary summary (policy, *event_pp, path, false, false, false);
1531 4 : ASSERT_EQ (summary.get_num_ranges (), 1);
1532 :
1533 4 : print_path_summary_as_text (summary, text_output, true);
1534 4 : ASSERT_STREQ (" `foo': events 1-2 (depth 0)\n"
1535 : " (1): first `free'\n"
1536 : " (2): double `free'\n",
1537 : pp_formatted_text (text_output.get_printer ()));
1538 4 : }
1539 :
1540 : /* Verify that print_path_summary works on an interprocedural path. */
1541 :
1542 : static void
1543 4 : test_interprocedural_path_1 (pretty_printer *event_pp)
1544 : {
1545 4 : logical_locations::selftest::test_manager logical_loc_mgr;
1546 4 : test_path path (logical_loc_mgr, event_pp);
1547 4 : path.add_entry ("test", 0);
1548 4 : path.add_call ("test", 0, "make_boxed_int");
1549 4 : path.add_call ("make_boxed_int", 1, "wrapped_malloc");
1550 4 : path.add_event (UNKNOWN_LOCATION,
1551 : "wrapped_malloc", 2, "calling malloc");
1552 4 : path.add_return ("test", 0);
1553 4 : path.add_call ("test", 0, "free_boxed_int");
1554 4 : path.add_call ("free_boxed_int", 1, "wrapped_free");
1555 4 : path.add_event (UNKNOWN_LOCATION, "wrapped_free", 2, "calling free");
1556 4 : path.add_return ("test", 0);
1557 4 : path.add_call ("test", 0, "free_boxed_int");
1558 4 : path.add_call ("free_boxed_int", 1, "wrapped_free");
1559 4 : path.add_event (UNKNOWN_LOCATION, "wrapped_free", 2, "calling free");
1560 4 : ASSERT_EQ (path.num_events (), 18);
1561 :
1562 4 : ASSERT_TRUE (path.interprocedural_p ());
1563 :
1564 4 : {
1565 4 : selftest::test_context dc;
1566 4 : text_sink text_output (dc, nullptr, false);
1567 4 : path_print_policy policy (text_output);
1568 4 : path_summary summary (policy, *event_pp, path, false);
1569 4 : ASSERT_EQ (summary.get_num_ranges (), 9);
1570 :
1571 4 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
1572 4 : print_path_summary_as_text (summary, text_output, true);
1573 4 : ASSERT_STREQ
1574 : (" `test': events 1-2 (depth 0)\n"
1575 : " |\n"
1576 : " | (1): entering `test'\n"
1577 : " | (2): calling `make_boxed_int'\n"
1578 : " |\n"
1579 : " +--> `make_boxed_int': events 3-4 (depth 1)\n"
1580 : " |\n"
1581 : " | (3): entering `make_boxed_int'\n"
1582 : " | (4): calling `wrapped_malloc'\n"
1583 : " |\n"
1584 : " +--> `wrapped_malloc': events 5-6 (depth 2)\n"
1585 : " |\n"
1586 : " | (5): entering `wrapped_malloc'\n"
1587 : " | (6): calling malloc\n"
1588 : " |\n"
1589 : " <-------------+\n"
1590 : " |\n"
1591 : " `test': events 7-8 (depth 0)\n"
1592 : " |\n"
1593 : " | (7): returning to `test'\n"
1594 : " | (8): calling `free_boxed_int'\n"
1595 : " |\n"
1596 : " +--> `free_boxed_int': events 9-10 (depth 1)\n"
1597 : " |\n"
1598 : " | (9): entering `free_boxed_int'\n"
1599 : " | (10): calling `wrapped_free'\n"
1600 : " |\n"
1601 : " +--> `wrapped_free': events 11-12 (depth 2)\n"
1602 : " |\n"
1603 : " | (11): entering `wrapped_free'\n"
1604 : " | (12): calling free\n"
1605 : " |\n"
1606 : " <-------------+\n"
1607 : " |\n"
1608 : " `test': events 13-14 (depth 0)\n"
1609 : " |\n"
1610 : " | (13): returning to `test'\n"
1611 : " | (14): calling `free_boxed_int'\n"
1612 : " |\n"
1613 : " +--> `free_boxed_int': events 15-16 (depth 1)\n"
1614 : " |\n"
1615 : " | (15): entering `free_boxed_int'\n"
1616 : " | (16): calling `wrapped_free'\n"
1617 : " |\n"
1618 : " +--> `wrapped_free': events 17-18 (depth 2)\n"
1619 : " |\n"
1620 : " | (17): entering `wrapped_free'\n"
1621 : " | (18): calling free\n"
1622 : " |\n",
1623 : pp_formatted_text (text_output.get_printer ()));
1624 4 : }
1625 4 : {
1626 4 : selftest::test_context dc;
1627 4 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE);
1628 4 : text_sink text_output (dc);
1629 4 : path_print_policy policy (text_output);
1630 4 : path_summary summary (policy, *event_pp, path, false);
1631 4 : print_path_summary_as_text (summary, text_output, true);
1632 4 : ASSERT_STREQ
1633 : (" `test': events 1-2 (depth 0)\n"
1634 : " │\n"
1635 : " │ (1): entering `test'\n"
1636 : " │ (2): calling `make_boxed_int'\n"
1637 : " │\n"
1638 : " └──> `make_boxed_int': events 3-4 (depth 1)\n"
1639 : " │\n"
1640 : " │ (3): entering `make_boxed_int'\n"
1641 : " │ (4): calling `wrapped_malloc'\n"
1642 : " │\n"
1643 : " └──> `wrapped_malloc': events 5-6 (depth 2)\n"
1644 : " │\n"
1645 : " │ (5): entering `wrapped_malloc'\n"
1646 : " │ (6): calling malloc\n"
1647 : " │\n"
1648 : " <─────────────┘\n"
1649 : " │\n"
1650 : " `test': events 7-8 (depth 0)\n"
1651 : " │\n"
1652 : " │ (7): returning to `test'\n"
1653 : " │ (8): calling `free_boxed_int'\n"
1654 : " │\n"
1655 : " └──> `free_boxed_int': events 9-10 (depth 1)\n"
1656 : " │\n"
1657 : " │ (9): entering `free_boxed_int'\n"
1658 : " │ (10): calling `wrapped_free'\n"
1659 : " │\n"
1660 : " └──> `wrapped_free': events 11-12 (depth 2)\n"
1661 : " │\n"
1662 : " │ (11): entering `wrapped_free'\n"
1663 : " │ (12): calling free\n"
1664 : " │\n"
1665 : " <─────────────┘\n"
1666 : " │\n"
1667 : " `test': events 13-14 (depth 0)\n"
1668 : " │\n"
1669 : " │ (13): returning to `test'\n"
1670 : " │ (14): calling `free_boxed_int'\n"
1671 : " │\n"
1672 : " └──> `free_boxed_int': events 15-16 (depth 1)\n"
1673 : " │\n"
1674 : " │ (15): entering `free_boxed_int'\n"
1675 : " │ (16): calling `wrapped_free'\n"
1676 : " │\n"
1677 : " └──> `wrapped_free': events 17-18 (depth 2)\n"
1678 : " │\n"
1679 : " │ (17): entering `wrapped_free'\n"
1680 : " │ (18): calling free\n"
1681 : " │\n",
1682 : pp_formatted_text (text_output.get_printer ()));
1683 4 : }
1684 :
1685 4 : }
1686 :
1687 : /* Example where we pop the stack to an intermediate frame, rather than the
1688 : initial one. */
1689 :
1690 : static void
1691 4 : test_interprocedural_path_2 (pretty_printer *event_pp)
1692 : {
1693 4 : logical_locations::selftest::test_manager logical_loc_mgr;
1694 4 : test_path path (logical_loc_mgr, event_pp);
1695 4 : path.add_entry ("foo", 0);
1696 4 : path.add_call ("foo", 0, "bar");
1697 4 : path.add_call ("bar", 1, "baz");
1698 4 : path.add_return ("bar", 1);
1699 4 : path.add_call ("bar", 1, "baz");
1700 4 : ASSERT_EQ (path.num_events (), 8);
1701 :
1702 4 : ASSERT_TRUE (path.interprocedural_p ());
1703 :
1704 4 : {
1705 4 : selftest::test_context dc;
1706 4 : text_sink text_output (dc);
1707 4 : path_print_policy policy (text_output);
1708 4 : path_summary summary (policy, *event_pp, path, false);
1709 4 : ASSERT_EQ (summary.get_num_ranges (), 5);
1710 4 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
1711 4 : print_path_summary_as_text (summary, text_output, true);
1712 4 : ASSERT_STREQ
1713 : (" `foo': events 1-2 (depth 0)\n"
1714 : " |\n"
1715 : " | (1): entering `foo'\n"
1716 : " | (2): calling `bar'\n"
1717 : " |\n"
1718 : " +--> `bar': events 3-4 (depth 1)\n"
1719 : " |\n"
1720 : " | (3): entering `bar'\n"
1721 : " | (4): calling `baz'\n"
1722 : " |\n"
1723 : " +--> `baz': event 5 (depth 2)\n"
1724 : " |\n"
1725 : " | (5): entering `baz'\n"
1726 : " |\n"
1727 : " <------+\n"
1728 : " |\n"
1729 : " `bar': events 6-7 (depth 1)\n"
1730 : " |\n"
1731 : " | (6): returning to `bar'\n"
1732 : " | (7): calling `baz'\n"
1733 : " |\n"
1734 : " +--> `baz': event 8 (depth 2)\n"
1735 : " |\n"
1736 : " | (8): entering `baz'\n"
1737 : " |\n",
1738 : pp_formatted_text (text_output.get_printer ()));
1739 4 : }
1740 4 : {
1741 4 : selftest::test_context dc;
1742 4 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE);
1743 4 : text_sink text_output (dc);
1744 4 : path_print_policy policy (text_output);
1745 4 : path_summary summary (policy, *event_pp, path, false);
1746 4 : print_path_summary_as_text (summary, text_output, true);
1747 4 : ASSERT_STREQ
1748 : (" `foo': events 1-2 (depth 0)\n"
1749 : " │\n"
1750 : " │ (1): entering `foo'\n"
1751 : " │ (2): calling `bar'\n"
1752 : " │\n"
1753 : " └──> `bar': events 3-4 (depth 1)\n"
1754 : " │\n"
1755 : " │ (3): entering `bar'\n"
1756 : " │ (4): calling `baz'\n"
1757 : " │\n"
1758 : " └──> `baz': event 5 (depth 2)\n"
1759 : " │\n"
1760 : " │ (5): entering `baz'\n"
1761 : " │\n"
1762 : " <──────┘\n"
1763 : " │\n"
1764 : " `bar': events 6-7 (depth 1)\n"
1765 : " │\n"
1766 : " │ (6): returning to `bar'\n"
1767 : " │ (7): calling `baz'\n"
1768 : " │\n"
1769 : " └──> `baz': event 8 (depth 2)\n"
1770 : " │\n"
1771 : " │ (8): entering `baz'\n"
1772 : " │\n",
1773 : pp_formatted_text (text_output.get_printer ()));
1774 4 : }
1775 4 : }
1776 :
1777 : /* Verify that print_path_summary is sane in the face of a recursive
1778 : diagnostic path. */
1779 :
1780 : static void
1781 4 : test_recursion (pretty_printer *event_pp)
1782 : {
1783 4 : logical_locations::selftest::test_manager logical_loc_mgr;
1784 4 : test_path path (logical_loc_mgr, event_pp);
1785 4 : path.add_entry ("factorial", 0);
1786 16 : for (int depth = 0; depth < 3; depth++)
1787 12 : path.add_call ("factorial", depth, "factorial");
1788 4 : ASSERT_EQ (path.num_events (), 7);
1789 :
1790 4 : ASSERT_TRUE (path.interprocedural_p ());
1791 :
1792 4 : {
1793 4 : selftest::test_context dc;
1794 4 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
1795 :
1796 4 : text_sink text_output (dc);
1797 4 : path_print_policy policy (text_output);
1798 4 : path_summary summary (policy, *event_pp, path, false);
1799 4 : ASSERT_EQ (summary.get_num_ranges (), 4);
1800 :
1801 4 : print_path_summary_as_text (summary, text_output, true);
1802 4 : ASSERT_STREQ
1803 : (" `factorial': events 1-2 (depth 0)\n"
1804 : " |\n"
1805 : " | (1): entering `factorial'\n"
1806 : " | (2): calling `factorial'\n"
1807 : " |\n"
1808 : " +--> `factorial': events 3-4 (depth 1)\n"
1809 : " |\n"
1810 : " | (3): entering `factorial'\n"
1811 : " | (4): calling `factorial'\n"
1812 : " |\n"
1813 : " +--> `factorial': events 5-6 (depth 2)\n"
1814 : " |\n"
1815 : " | (5): entering `factorial'\n"
1816 : " | (6): calling `factorial'\n"
1817 : " |\n"
1818 : " +--> `factorial': event 7 (depth 3)\n"
1819 : " |\n"
1820 : " | (7): entering `factorial'\n"
1821 : " |\n",
1822 : pp_formatted_text (text_output.get_printer ()));
1823 4 : }
1824 4 : {
1825 4 : selftest::test_context dc;
1826 4 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE);
1827 :
1828 4 : text_sink text_output (dc);
1829 4 : path_print_policy policy (text_output);
1830 4 : path_summary summary (policy, *event_pp, path, false);
1831 4 : print_path_summary_as_text (summary, text_output, true);
1832 4 : ASSERT_STREQ
1833 : (" `factorial': events 1-2 (depth 0)\n"
1834 : " │\n"
1835 : " │ (1): entering `factorial'\n"
1836 : " │ (2): calling `factorial'\n"
1837 : " │\n"
1838 : " └──> `factorial': events 3-4 (depth 1)\n"
1839 : " │\n"
1840 : " │ (3): entering `factorial'\n"
1841 : " │ (4): calling `factorial'\n"
1842 : " │\n"
1843 : " └──> `factorial': events 5-6 (depth 2)\n"
1844 : " │\n"
1845 : " │ (5): entering `factorial'\n"
1846 : " │ (6): calling `factorial'\n"
1847 : " │\n"
1848 : " └──> `factorial': event 7 (depth 3)\n"
1849 : " │\n"
1850 : " │ (7): entering `factorial'\n"
1851 : " │\n",
1852 : pp_formatted_text (text_output.get_printer ()));
1853 4 : }
1854 4 : }
1855 :
1856 : /* Helper class for writing tests of control flow visualization. */
1857 :
1858 576 : class control_flow_test
1859 : {
1860 : public:
1861 576 : control_flow_test (const selftest::location &loc,
1862 : const line_table_case &case_,
1863 : const char *content)
1864 576 : : m_tmp_file (loc, ".c", content,
1865 : /* gcc_rich_location::add_location_if_nearby implicitly
1866 : uses global_dc's file_cache, so we need to evict
1867 : tmp when we're done. */
1868 576 : &global_dc->get_file_cache ()),
1869 576 : m_ltt (case_)
1870 : {
1871 576 : m_ord_map
1872 576 : = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
1873 : m_tmp_file.get_filename (), 0));
1874 576 : linemap_line_start (line_table, 1, 100);
1875 576 : }
1876 :
1877 10272 : location_t get_line_and_column (int line, int column)
1878 : {
1879 10272 : return linemap_position_for_line_and_column (line_table, m_ord_map,
1880 : line, column);
1881 : }
1882 :
1883 2592 : location_t get_line_and_columns (int line, int first_column, int last_column)
1884 : {
1885 2592 : return get_line_and_columns (line,
1886 : first_column, first_column, last_column);
1887 : }
1888 :
1889 3264 : location_t get_line_and_columns (int line,
1890 : int first_column,
1891 : int caret_column,
1892 : int last_column)
1893 : {
1894 3264 : return make_location (get_line_and_column (line, caret_column),
1895 : get_line_and_column (line, first_column),
1896 3264 : get_line_and_column (line, last_column));
1897 : }
1898 :
1899 : private:
1900 : temp_source_file m_tmp_file;
1901 : line_table_test m_ltt;
1902 : const line_map_ordinary *m_ord_map;
1903 : };
1904 :
1905 : /* Example of event edges where all events can go in the same layout,
1906 : testing the 6 combinations of:
1907 : - ASCII vs Unicode vs event links off
1908 : - line numbering on and off. */
1909 :
1910 : static void
1911 96 : test_control_flow_1 (const line_table_case &case_,
1912 : pretty_printer *event_pp)
1913 : {
1914 : /* Create a tempfile and write some text to it.
1915 : ...000000000111111111122222222223333333333.
1916 : ...123456789012345678901234567890123456789. */
1917 96 : const char *content
1918 : = ("int test (int *p)\n" /* line 1. */
1919 : "{\n" /* line 2. */
1920 : " if (p)\n" /* line 3. */
1921 : " return 0;\n" /* line 4. */
1922 : " return *p;\n" /* line 5. */
1923 : "}\n"); /* line 6. */
1924 :
1925 96 : control_flow_test t (SELFTEST_LOCATION, case_, content);
1926 :
1927 96 : const location_t conditional = t.get_line_and_column (3, 7);
1928 96 : const location_t cfg_dest = t.get_line_and_column (5, 10);
1929 :
1930 96 : logical_locations::selftest::test_manager logical_loc_mgr;
1931 96 : test_path path (logical_loc_mgr, event_pp);
1932 96 : path.add_event (conditional, nullptr, 0,
1933 : "following %qs branch (when %qs is NULL)...",
1934 : "false", "p");
1935 96 : path.connect_to_next_event ();
1936 :
1937 96 : path.add_event (cfg_dest, nullptr, 0,
1938 : "...to here");
1939 96 : path.add_event (cfg_dest, nullptr, 0,
1940 : "dereference of NULL %qs",
1941 : "p");
1942 :
1943 96 : if (!path_events_have_column_data_p (path))
1944 40 : return;
1945 :
1946 :
1947 56 : {
1948 56 : selftest::test_context dc;
1949 56 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
1950 56 : dc.show_event_links (true);
1951 56 : text_sink text_output (dc);
1952 56 : path_print_policy policy (text_output);
1953 56 : path_summary summary (policy, *event_pp, path, true);
1954 56 : print_path_summary_as_text (summary, text_output, false);
1955 56 : ASSERT_STREQ
1956 : (" events 1-3\n"
1957 : "FILENAME:3:7:\n"
1958 : " if (p)\n"
1959 : " ^\n"
1960 : " |\n"
1961 : " (1) following `false' branch (when `p' is NULL)... ->-+\n"
1962 : " |\n"
1963 : "FILENAME:5:10:\n"
1964 : " |\n"
1965 : "+------------------------------------------------------------+\n"
1966 : "| return *p;\n"
1967 : "| ~\n"
1968 : "| |\n"
1969 : "+-------->(2) ...to here\n"
1970 : " (3) dereference of NULL `p'\n",
1971 : pp_formatted_text (text_output.get_printer ()));
1972 56 : }
1973 56 : {
1974 56 : selftest::test_context dc;
1975 56 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
1976 56 : dc.show_event_links (false);
1977 56 : text_sink text_output (dc);
1978 56 : path_print_policy policy (text_output);
1979 56 : path_summary summary (policy, *event_pp, path, true);
1980 56 : print_path_summary_as_text (summary, text_output, false);
1981 56 : ASSERT_STREQ
1982 : (" events 1-3\n"
1983 : "FILENAME:3:7:\n"
1984 : " if (p)\n"
1985 : " ^\n"
1986 : " |\n"
1987 : " (1) following `false' branch (when `p' is NULL)...\n"
1988 : "FILENAME:5:10:\n"
1989 : " return *p;\n"
1990 : " ~\n"
1991 : " |\n"
1992 : " (2) ...to here\n"
1993 : " (3) dereference of NULL `p'\n",
1994 : pp_formatted_text (text_output.get_printer ()));
1995 56 : }
1996 56 : {
1997 56 : selftest::test_context dc;
1998 56 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
1999 56 : dc.show_line_numbers (true);
2000 56 : dc.show_event_links (true);
2001 56 : text_sink text_output (dc);
2002 56 : path_print_policy policy (text_output);
2003 56 : path_summary summary (policy, *event_pp, path, true);
2004 56 : print_path_summary_as_text (summary, text_output, false);
2005 56 : ASSERT_STREQ
2006 : (" events 1-3\n"
2007 : "FILENAME:3:7:\n"
2008 : " 3 | if (p)\n"
2009 : " | ^\n"
2010 : " | |\n"
2011 : " | (1) following `false' branch (when `p' is NULL)... ->-+\n"
2012 : " | |\n"
2013 : " | |\n"
2014 : " |+------------------------------------------------------------+\n"
2015 : " 4 || return 0;\n"
2016 : " 5 || return *p;\n"
2017 : " || ~\n"
2018 : " || |\n"
2019 : " |+-------->(2) ...to here\n"
2020 : " | (3) dereference of NULL `p'\n",
2021 : pp_formatted_text (text_output.get_printer ()));
2022 56 : }
2023 56 : {
2024 56 : selftest::test_context dc;
2025 56 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
2026 56 : dc.show_line_numbers (true);
2027 56 : dc.show_event_links (false);
2028 56 : text_sink text_output (dc);
2029 56 : path_print_policy policy (text_output);
2030 56 : path_summary summary (policy, *event_pp, path, true);
2031 56 : print_path_summary_as_text (summary, text_output, false);
2032 56 : ASSERT_STREQ
2033 : (" events 1-3\n"
2034 : "FILENAME:3:7:\n"
2035 : " 3 | if (p)\n"
2036 : " | ^\n"
2037 : " | |\n"
2038 : " | (1) following `false' branch (when `p' is NULL)...\n"
2039 : " 4 | return 0;\n"
2040 : " 5 | return *p;\n"
2041 : " | ~\n"
2042 : " | |\n"
2043 : " | (2) ...to here\n"
2044 : " | (3) dereference of NULL `p'\n",
2045 : pp_formatted_text (text_output.get_printer ()));
2046 56 : }
2047 56 : {
2048 56 : selftest::test_context dc;
2049 56 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE);
2050 56 : dc.show_event_links (true);
2051 56 : text_sink text_output (dc);
2052 56 : path_print_policy policy (text_output);
2053 56 : path_summary summary (policy, *event_pp, path, true);
2054 56 : print_path_summary_as_text (summary, text_output, false);
2055 56 : ASSERT_STREQ
2056 : (" events 1-3\n"
2057 : "FILENAME:3:7:\n"
2058 : " if (p)\n"
2059 : " ^\n"
2060 : " |\n"
2061 : " (1) following `false' branch (when `p' is NULL)... ─>─┐\n"
2062 : " │\n"
2063 : "FILENAME:5:10:\n"
2064 : " │\n"
2065 : "┌────────────────────────────────────────────────────────────┘\n"
2066 : "│ return *p;\n"
2067 : "│ ~\n"
2068 : "│ |\n"
2069 : "└────────>(2) ...to here\n"
2070 : " (3) dereference of NULL `p'\n",
2071 : pp_formatted_text (text_output.get_printer ()));
2072 56 : }
2073 56 : {
2074 56 : selftest::test_context dc;
2075 56 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE);
2076 56 : dc.show_event_links (true);
2077 56 : dc.show_line_numbers (true);
2078 56 : text_sink text_output (dc);
2079 56 : path_print_policy policy (text_output);
2080 56 : path_summary summary (policy, *event_pp, path, true);
2081 56 : print_path_summary_as_text (summary, text_output, false);
2082 56 : ASSERT_STREQ
2083 : (" events 1-3\n"
2084 : "FILENAME:3:7:\n"
2085 : " 3 | if (p)\n"
2086 : " | ^\n"
2087 : " | |\n"
2088 : " | (1) following `false' branch (when `p' is NULL)... ─>─┐\n"
2089 : " | │\n"
2090 : " | │\n"
2091 : " |┌────────────────────────────────────────────────────────────┘\n"
2092 : " 4 |│ return 0;\n"
2093 : " 5 |│ return *p;\n"
2094 : " |│ ~\n"
2095 : " |│ |\n"
2096 : " |└────────>(2) ...to here\n"
2097 : " | (3) dereference of NULL `p'\n",
2098 : pp_formatted_text (text_output.get_printer ()));
2099 56 : }
2100 192 : }
2101 :
2102 : /* Complex example involving a backedge. */
2103 :
2104 : static void
2105 96 : test_control_flow_2 (const line_table_case &case_,
2106 : pretty_printer *event_pp)
2107 : {
2108 : /* Create a tempfile and write some text to it.
2109 : ...000000000111111111122222222223333333333.
2110 : ...123456789012345678901234567890123456789. */
2111 96 : const char *content
2112 : = ("int for_loop_noop_next (struct node *n)\n" /* <--------- line 1. */
2113 : "{\n" /* <----------------------------------------------- line 2. */
2114 : " int sum = 0;\n" /* <---------------------------------- line 3. */
2115 : " for (struct node *iter = n; iter; iter->next)\n" /* <- line 4. */
2116 : " sum += n->val;\n" /* <------------------------------ line 5. */
2117 : " return sum;\n" /* <----------------------------------- line 6. */
2118 : "}\n"); /* <-------------------------------------------- line 7. */
2119 : /* Adapted from infinite-loop-linked-list.c where
2120 : "iter->next" should be "iter = iter->next". */
2121 :
2122 96 : control_flow_test t (SELFTEST_LOCATION, case_, content);
2123 :
2124 96 : const location_t iter_test = t.get_line_and_columns (4, 31, 34);
2125 96 : const location_t loop_body_start = t.get_line_and_columns (5, 12, 17);
2126 96 : const location_t loop_body_end = t.get_line_and_columns (5, 5, 9, 17);
2127 :
2128 96 : logical_locations::selftest::test_manager logical_loc_mgr;
2129 96 : test_path path (logical_loc_mgr, event_pp);
2130 96 : path.add_event (iter_test, nullptr, 0, "infinite loop here");
2131 :
2132 96 : path.add_event (iter_test, nullptr, 0, "looping from here...");
2133 96 : path.connect_to_next_event ();
2134 :
2135 96 : path.add_event (loop_body_start, nullptr, 0, "...to here");
2136 :
2137 96 : path.add_event (loop_body_end, nullptr, 0, "looping back...");
2138 96 : path.connect_to_next_event ();
2139 :
2140 96 : path.add_event (iter_test, nullptr, 0, "...to here");
2141 :
2142 96 : if (!path_events_have_column_data_p (path))
2143 40 : return;
2144 :
2145 56 : {
2146 56 : selftest::test_context dc;
2147 56 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
2148 56 : dc.show_event_links (true);
2149 56 : dc.show_line_numbers (true);
2150 56 : text_sink text_output (dc);
2151 56 : path_print_policy policy (text_output);
2152 56 : path_summary summary (policy, *event_pp, path, true);
2153 56 : print_path_summary_as_text (summary, text_output, false);
2154 56 : ASSERT_STREQ
2155 : (" events 1-3\n"
2156 : "FILENAME:4:31:\n"
2157 : " 4 | for (struct node *iter = n; iter; iter->next)\n"
2158 : " | ^~~~\n"
2159 : " | |\n"
2160 : " | (1) infinite loop here\n"
2161 : " | (2) looping from here... ->-+\n"
2162 : " | |\n"
2163 : " | |\n"
2164 : " |+----------------------------------------------------------+\n"
2165 : " 5 || sum += n->val;\n"
2166 : " || ~~~~~~ \n"
2167 : " || |\n"
2168 : " |+---------->(3) ...to here\n"
2169 : /* We need to start an new event_range here as event (4) is to the
2170 : left of event (3), and thus (4) would mess up the in-edge to (3). */
2171 : " event 4\n"
2172 : " 5 | sum += n->val;\n"
2173 : " | ~~~~^~~~~~~~~\n"
2174 : " | |\n"
2175 : " | (4) looping back... ->-+\n"
2176 : " | |\n"
2177 : /* We need to start an new event_range here as event (4) with an
2178 : out-edge is on a later line (line 5) than its destination event (5),
2179 : on line 4. */
2180 : " event 5\n"
2181 : " | |\n"
2182 : " |+-------------------------------+\n"
2183 : " 4 || for (struct node *iter = n; iter; iter->next)\n"
2184 : " || ^~~~\n"
2185 : " || |\n"
2186 : " |+----------------------------->(5) ...to here\n",
2187 : pp_formatted_text (text_output.get_printer ()));
2188 56 : }
2189 192 : }
2190 :
2191 : /* Complex example involving a backedge and both an in-edge and out-edge
2192 : on the same line. */
2193 :
2194 : static void
2195 96 : test_control_flow_3 (const line_table_case &case_,
2196 : pretty_printer *event_pp)
2197 : {
2198 : /* Create a tempfile and write some text to it.
2199 : ...000000000111111111122222222223333333333.
2200 : ...123456789012345678901234567890123456789. */
2201 96 : const char *content
2202 : = ("void test_missing_comparison_in_for_condition_1 (int n)\n"
2203 : "{\n" /* <------------------------- line 2. */
2204 : " for (int i = 0; n; i++)\n" /* <- line 3. */
2205 : " {\n" /* <--------------------- line 4. */
2206 : " }\n" /* <--------------------- line 5. */
2207 : "}\n"); /* <----------------------- line 6. */
2208 : /* Adapted from infinite-loop-1.c where the condition should have been
2209 : "i < n", rather than just "n". */
2210 :
2211 96 : control_flow_test t (SELFTEST_LOCATION, case_, content);
2212 :
2213 96 : const location_t iter_test = t.get_line_and_column (3, 19);
2214 96 : const location_t iter_next = t.get_line_and_columns (3, 22, 24);
2215 :
2216 96 : logical_locations::selftest::test_manager logical_loc_mgr;
2217 96 : test_path path (logical_loc_mgr, event_pp);
2218 96 : path.add_event (iter_test, nullptr, 0, "infinite loop here");
2219 :
2220 96 : path.add_event (iter_test, nullptr, 0, "looping from here...");
2221 96 : path.connect_to_next_event ();
2222 :
2223 96 : path.add_event (iter_next, nullptr, 0, "...to here");
2224 :
2225 96 : path.add_event (iter_next, nullptr, 0, "looping back...");
2226 96 : path.connect_to_next_event ();
2227 :
2228 96 : path.add_event (iter_test, nullptr, 0, "...to here");
2229 :
2230 96 : if (!path_events_have_column_data_p (path))
2231 36 : return;
2232 :
2233 60 : {
2234 60 : selftest::test_context dc;
2235 60 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
2236 60 : dc.show_event_links (true);
2237 60 : dc.show_line_numbers (true);
2238 60 : text_sink text_output (dc);
2239 60 : path_print_policy policy (text_output);
2240 60 : path_summary summary (policy, *event_pp, path, true);
2241 60 : print_path_summary_as_text (summary, text_output, false);
2242 60 : ASSERT_STREQ
2243 : (" events 1-2\n"
2244 : "FILENAME:3:19:\n"
2245 : " 3 | for (int i = 0; n; i++)\n"
2246 : " | ^\n"
2247 : " | |\n"
2248 : " | (1) infinite loop here\n"
2249 : " | (2) looping from here... ->-+\n"
2250 : " | |\n"
2251 : " events 3-4\n"
2252 : " | |\n"
2253 : " |+----------------------------------------------+\n"
2254 : " 3 || for (int i = 0; n; i++)\n"
2255 : " || ^~~\n"
2256 : " || |\n"
2257 : " |+-------------------->(3) ...to here\n"
2258 : " | (4) looping back... ->-+\n"
2259 : " | |\n"
2260 : /* We need to start an new event_range here as event (4) with an
2261 : out-edge is on the same line as its destination event (5), but
2262 : to the right, which we can't handle as a single event_range. */
2263 : " event 5\n"
2264 : " | |\n"
2265 : " |+--------------------------------------------+\n"
2266 : " 3 || for (int i = 0; n; i++)\n"
2267 : " || ^\n"
2268 : " || |\n"
2269 : " |+----------------->(5) ...to here\n",
2270 : pp_formatted_text (text_output.get_printer ()));
2271 60 : }
2272 192 : }
2273 :
2274 : /* Implementation of ASSERT_CFG_EDGE_PATH_STREQ. */
2275 :
2276 : static void
2277 1152 : assert_cfg_edge_path_streq (const location &loc,
2278 : pretty_printer *event_pp,
2279 : const location_t src_loc,
2280 : const location_t dst_loc,
2281 : const char *expected_str)
2282 : {
2283 1152 : logical_locations::selftest::test_manager logical_loc_mgr;
2284 1152 : test_path path (logical_loc_mgr, event_pp);
2285 1152 : path.add_event (src_loc, nullptr, 0, "from here...");
2286 1152 : path.connect_to_next_event ();
2287 :
2288 1152 : path.add_event (dst_loc, nullptr, 0, "...to here");
2289 :
2290 1152 : if (!path_events_have_column_data_p (path))
2291 464 : return;
2292 :
2293 688 : selftest::test_context dc;
2294 688 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
2295 688 : dc.show_event_links (true);
2296 688 : dc.show_line_numbers (true);
2297 688 : text_sink text_output (dc);
2298 688 : path_print_policy policy (text_output);
2299 688 : path_summary summary (policy, *event_pp, path, true);
2300 688 : print_path_summary_as_text (summary, text_output, false);
2301 688 : ASSERT_STREQ_AT (loc, expected_str,
2302 : pp_formatted_text (text_output.get_printer ()));
2303 1152 : }
2304 :
2305 : /* Assert that if we make a path with an event with "from here..." at SRC_LOC
2306 : leading to an event "...to here" at DST_LOC that we print the path
2307 : as EXPECTED_STR. */
2308 :
2309 : #define ASSERT_CFG_EDGE_PATH_STREQ(SRC_LOC, DST_LOC, EXPECTED_STR) \
2310 : assert_cfg_edge_path_streq ((SELFTEST_LOCATION), (event_pp), \
2311 : (SRC_LOC), (DST_LOC), (EXPECTED_STR))
2312 :
2313 : /* Various examples of edge, trying to cover all combinations of:
2314 : - relative x positive of src label and dst label
2315 : - relative y position of labels:
2316 : - on same line
2317 : - on next line
2318 : - on line after next
2319 : - big gap, where src is before dst
2320 : - big gap, where src is after dst
2321 : and other awkward cases. */
2322 :
2323 : static void
2324 96 : test_control_flow_4 (const line_table_case &case_,
2325 : pretty_printer *event_pp)
2326 : {
2327 96 : std::string many_lines;
2328 9696 : for (int i = 1; i <= 100; i++)
2329 : /* ............000000000111
2330 : ............123456789012. */
2331 9600 : many_lines += "LHS RHS\n";
2332 96 : control_flow_test t (SELFTEST_LOCATION, case_, many_lines.c_str ());
2333 :
2334 : /* Same line. */
2335 96 : {
2336 : /* LHS -> RHS. */
2337 96 : ASSERT_CFG_EDGE_PATH_STREQ
2338 : (t.get_line_and_columns (3, 1, 3),
2339 : t.get_line_and_columns (3, 10, 12),
2340 : (" event 1\n"
2341 : "FILENAME:3:1:\n"
2342 : " 3 | LHS RHS\n"
2343 : " | ^~~\n"
2344 : " | |\n"
2345 : " | (1) from here... ->-+\n"
2346 : " | |\n"
2347 : " event 2\n"
2348 : " | |\n"
2349 : " |+--------------------+\n"
2350 : " 3 ||LHS RHS\n"
2351 : " || ^~~\n"
2352 : " || |\n"
2353 : " |+-------->(2) ...to here\n"));
2354 :
2355 : /* RHS -> LHS. */
2356 96 : ASSERT_CFG_EDGE_PATH_STREQ
2357 : (t.get_line_and_columns (3, 10, 12),
2358 : t.get_line_and_columns (3, 1, 3),
2359 : (" event 1\n"
2360 : "FILENAME:3:10:\n"
2361 : " 3 | LHS RHS\n"
2362 : " | ^~~\n"
2363 : " | |\n"
2364 : " | (1) from here... ->-+\n"
2365 : " | |\n"
2366 : " event 2\n"
2367 : " | |\n"
2368 : " |+-----------------------------+\n"
2369 : " 3 ||LHS RHS\n"
2370 : " ||^~~\n"
2371 : " |||\n"
2372 : " |+(2) ...to here\n"));
2373 : }
2374 :
2375 : /* Next line. */
2376 96 : {
2377 : /* LHS -> RHS. */
2378 96 : ASSERT_CFG_EDGE_PATH_STREQ
2379 : (t.get_line_and_columns (3, 1, 3),
2380 : t.get_line_and_columns (4, 5, 7),
2381 : (" events 1-2\n"
2382 : "FILENAME:3:1:\n"
2383 : " 3 | LHS RHS\n"
2384 : " | ^~~\n"
2385 : " | |\n"
2386 : " | (1) from here... ->-+\n"
2387 : " | |\n"
2388 : " | |\n"
2389 : " |+--------------------+\n"
2390 : " 4 ||LHS RHS\n"
2391 : " || ~~~\n"
2392 : " || |\n"
2393 : " |+--->(2) ...to here\n"));
2394 :
2395 : /* RHS -> LHS. */
2396 96 : ASSERT_CFG_EDGE_PATH_STREQ
2397 : (t.get_line_and_columns (3, 10, 12),
2398 : t.get_line_and_columns (4, 1, 3),
2399 : (" events 1-2\n"
2400 : "FILENAME:3:10:\n"
2401 : " 3 | LHS RHS\n"
2402 : " | ^~~\n"
2403 : " | |\n"
2404 : " | (1) from here... ->-+\n"
2405 : " | |\n"
2406 : " | |\n"
2407 : " |+-----------------------------+\n"
2408 : " 4 ||LHS RHS\n"
2409 : " ||~~~ \n"
2410 : " |||\n"
2411 : " |+(2) ...to here\n"));
2412 : }
2413 :
2414 : /* Line after next. */
2415 96 : {
2416 : /* LHS -> RHS. */
2417 96 : ASSERT_CFG_EDGE_PATH_STREQ
2418 : (t.get_line_and_columns (3, 1, 3),
2419 : t.get_line_and_columns (5, 10, 12),
2420 : (" events 1-2\n"
2421 : "FILENAME:3:1:\n"
2422 : " 3 | LHS RHS\n"
2423 : " | ^~~\n"
2424 : " | |\n"
2425 : " | (1) from here... ->-+\n"
2426 : " | |\n"
2427 : " | |\n"
2428 : " |+--------------------+\n"
2429 : " 4 ||LHS RHS\n"
2430 : " 5 ||LHS RHS\n"
2431 : " || ~~~\n"
2432 : " || |\n"
2433 : " |+-------->(2) ...to here\n"));
2434 :
2435 : /* RHS -> LHS. */
2436 96 : ASSERT_CFG_EDGE_PATH_STREQ
2437 : (t.get_line_and_columns (3, 10, 12),
2438 : t.get_line_and_columns (5, 1, 3),
2439 : (" events 1-2\n"
2440 : "FILENAME:3:10:\n"
2441 : " 3 | LHS RHS\n"
2442 : " | ^~~\n"
2443 : " | |\n"
2444 : " | (1) from here... ->-+\n"
2445 : " | |\n"
2446 : " | |\n"
2447 : " |+-----------------------------+\n"
2448 : " 4 ||LHS RHS\n"
2449 : " 5 ||LHS RHS\n"
2450 : " ||~~~ \n"
2451 : " |||\n"
2452 : " |+(2) ...to here\n"));
2453 : }
2454 :
2455 : /* Big gap, increasing line number. */
2456 96 : {
2457 : /* LHS -> RHS. */
2458 96 : ASSERT_CFG_EDGE_PATH_STREQ
2459 : (t.get_line_and_columns (3, 1, 3),
2460 : t.get_line_and_columns (97, 10, 12),
2461 : (" events 1-2\n"
2462 : "FILENAME:3:1:\n"
2463 : " 3 | LHS RHS\n"
2464 : " | ^~~\n"
2465 : " | |\n"
2466 : " | (1) from here... ->-+\n"
2467 : " | |\n"
2468 : "......\n"
2469 : " | |\n"
2470 : " |+--------------------+\n"
2471 : " 97 ||LHS RHS\n"
2472 : " || ~~~\n"
2473 : " || |\n"
2474 : " |+-------->(2) ...to here\n"));
2475 :
2476 : /* RHS -> LHS. */
2477 96 : ASSERT_CFG_EDGE_PATH_STREQ
2478 : (t.get_line_and_columns (3, 10, 12),
2479 : t.get_line_and_columns (97, 1, 3),
2480 : (" events 1-2\n"
2481 : "FILENAME:3:10:\n"
2482 : " 3 | LHS RHS\n"
2483 : " | ^~~\n"
2484 : " | |\n"
2485 : " | (1) from here... ->-+\n"
2486 : " | |\n"
2487 : "......\n"
2488 : " | |\n"
2489 : " |+-----------------------------+\n"
2490 : " 97 ||LHS RHS\n"
2491 : " ||~~~ \n"
2492 : " |||\n"
2493 : " |+(2) ...to here\n"));
2494 : }
2495 :
2496 : /* Big gap, decreasing line number. */
2497 96 : {
2498 : /* LHS -> RHS. */
2499 96 : ASSERT_CFG_EDGE_PATH_STREQ
2500 : (t.get_line_and_columns (97, 1, 3),
2501 : t.get_line_and_columns (3, 10, 12),
2502 : (" event 1\n"
2503 : "FILENAME:97:1:\n"
2504 : " 97 | LHS RHS\n"
2505 : " | ^~~\n"
2506 : " | |\n"
2507 : " | (1) from here... ->-+\n"
2508 : " | |\n"
2509 : " event 2\n"
2510 : " | |\n"
2511 : " |+--------------------+\n"
2512 : " 3 ||LHS RHS\n"
2513 : " || ^~~\n"
2514 : " || |\n"
2515 : " |+-------->(2) ...to here\n"));
2516 :
2517 : /* RHS -> LHS. */
2518 96 : ASSERT_CFG_EDGE_PATH_STREQ
2519 : (t.get_line_and_columns (97, 10, 12),
2520 : t.get_line_and_columns (3, 1, 3),
2521 : (" event 1\n"
2522 : "FILENAME:97:10:\n"
2523 : " 97 | LHS RHS\n"
2524 : " | ^~~\n"
2525 : " | |\n"
2526 : " | (1) from here... ->-+\n"
2527 : " | |\n"
2528 : " event 2\n"
2529 : " | |\n"
2530 : " |+-----------------------------+\n"
2531 : " 3 ||LHS RHS\n"
2532 : " ||^~~\n"
2533 : " |||\n"
2534 : " |+(2) ...to here\n"));
2535 : }
2536 :
2537 : /* Unknown src. */
2538 96 : {
2539 96 : ASSERT_CFG_EDGE_PATH_STREQ
2540 : (UNKNOWN_LOCATION,
2541 : t.get_line_and_columns (3, 10, 12),
2542 : (" event 1\n"
2543 : " (1): from here...\n"
2544 : " event 2\n"
2545 : "FILENAME:3:10:\n"
2546 : " 3 | LHS RHS\n"
2547 : " | ^~~\n"
2548 : " | |\n"
2549 : " |+-------->(2) ...to here\n"));
2550 : }
2551 :
2552 : /* Unknown dst. */
2553 96 : {
2554 96 : ASSERT_CFG_EDGE_PATH_STREQ
2555 : (t.get_line_and_columns (3, 1, 3),
2556 : UNKNOWN_LOCATION,
2557 : (" event 1\n"
2558 : "FILENAME:3:1:\n"
2559 : " 3 | LHS RHS\n"
2560 : " | ^~~\n"
2561 : " | |\n"
2562 : " | (1) from here... ->-+\n"
2563 : " | |\n"
2564 : " event 2\n"
2565 : "FILENAME:\n"
2566 : " (2): ...to here\n"));
2567 : }
2568 96 : }
2569 :
2570 : /* Another complex example, adapted from data-model-20.c. */
2571 :
2572 : static void
2573 96 : test_control_flow_5 (const line_table_case &case_,
2574 : pretty_printer *event_pp)
2575 : {
2576 : /* Create a tempfile and write some text to it.
2577 : ...000000000111111111122222222223333333333444444444455555555556666666666.
2578 : ...123456789012345678901234567890123456789012345678901234567890123456789. */
2579 96 : const char *content
2580 : = (" if ((arr = (struct foo **)malloc(n * sizeof(struct foo *))) == NULL)\n"
2581 : " return NULL;\n" /* <------------------------- line 2. */
2582 : "\n" /* <----------------------------------------- line 3. */
2583 : " for (i = 0; i < n; i++) {\n" /* <-------------- line 4. */
2584 : " if ((arr[i] = (struct foo *)malloc(sizeof(struct foo))) == NULL) {\n");
2585 :
2586 96 : control_flow_test t (SELFTEST_LOCATION, case_, content);
2587 :
2588 96 : logical_locations::selftest::test_manager logical_loc_mgr;
2589 96 : test_path path (logical_loc_mgr, event_pp);
2590 : /* (1) */
2591 96 : path.add_event (t.get_line_and_column (1, 6), nullptr, 0,
2592 : "following %qs branch (when %qs is non-NULL)...",
2593 : "false", "arr");
2594 96 : path.connect_to_next_event ();
2595 :
2596 : /* (2) */
2597 96 : path.add_event (t.get_line_and_columns (4, 8, 10, 12), nullptr, 0,
2598 : "...to here");
2599 :
2600 : /* (3) */
2601 96 : path.add_event (t.get_line_and_columns (4, 15, 17, 19), nullptr, 0,
2602 : "following %qs branch (when %qs)...",
2603 : "true", "i < n");
2604 96 : path.connect_to_next_event ();
2605 :
2606 : /* (4) */
2607 96 : path.add_event (t.get_line_and_column (5, 13), nullptr, 0,
2608 : "...to here");
2609 :
2610 : /* (5) */
2611 96 : path.add_event (t.get_line_and_columns (5, 33, 58), nullptr, 0,
2612 : "allocated here");
2613 :
2614 96 : if (!path_events_have_column_data_p (path))
2615 40 : return;
2616 :
2617 56 : {
2618 56 : selftest::test_context dc;
2619 56 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
2620 56 : dc.show_event_links (true);
2621 56 : dc.show_line_numbers (true);
2622 56 : text_sink text_output (dc);
2623 56 : path_print_policy policy (text_output);
2624 56 : path_summary summary (policy, *event_pp, path, true);
2625 56 : print_path_summary_as_text (summary, text_output, false);
2626 56 : ASSERT_STREQ
2627 : (" events 1-5\n"
2628 : "FILENAME:1:6:\n"
2629 : " 1 | if ((arr = (struct foo **)malloc(n * sizeof(struct foo *))) == NULL)\n"
2630 : " | ^\n"
2631 : " | |\n"
2632 : " | (1) following `false' branch (when `arr' is non-NULL)... ->-+\n"
2633 : " | |\n"
2634 : "......\n"
2635 : " | |\n"
2636 : " |+-----------------------------------------------------------------+\n"
2637 : " 4 || for (i = 0; i < n; i++) {\n"
2638 : " || ~~~~~ ~~~~~\n"
2639 : " || | |\n"
2640 : " || | (3) following `true' branch (when `i < n')... ->-+\n"
2641 : " |+-------->(2) ...to here |\n"
2642 : " | |\n"
2643 : " | |\n"
2644 : " |+-----------------------------------------------------------------+\n"
2645 : " 5 || if ((arr[i] = (struct foo *)malloc(sizeof(struct foo))) == NULL) {\n"
2646 : " || ~ ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
2647 : " || | |\n"
2648 : " |+----------->(4) ...to here (5) allocated here\n",
2649 : pp_formatted_text (text_output.get_printer ()));
2650 56 : }
2651 192 : }
2652 :
2653 : /* Another complex example, adapted from loop-3.c. */
2654 :
2655 : static void
2656 96 : test_control_flow_6 (const line_table_case &case_,
2657 : pretty_printer *event_pp)
2658 : {
2659 : /* Create a tempfile and write some text to it.
2660 : ...000000000111111111122222222223333333.
2661 : ...123456789012345678901234567890123456. */
2662 96 : const char *content
2663 : = ("#include <stdlib.h>\n" /* <------------------ line 1. */
2664 : "\n" /* <------------------------------------- line 2. */
2665 : "void test(int c)\n" /* <--------------------- line 3. */
2666 : "{\n" /* <------------------------------------ line 4. */
2667 : " int i;\n" /* <----------------------------- line 5. */
2668 : " char *buffer = (char*)malloc(256);\n" /* <- line 6. */
2669 : "\n" /* <------------------------------------- line 7. */
2670 : " for (i=0; i<255; i++) {\n" /* <------------ line 8. */
2671 : " buffer[i] = c;\n" /* <------------------- line 9. */
2672 : "\n" /* <------------------------------------- line 10. */
2673 : " free(buffer);\n" /* <-------------------- line 11. */
2674 : " }\n"); /* <-------------------------------- line 12. */
2675 :
2676 96 : control_flow_test t (SELFTEST_LOCATION, case_, content);
2677 :
2678 96 : logical_locations::selftest::test_manager logical_loc_mgr;
2679 96 : test_path path (logical_loc_mgr, event_pp);
2680 : /* (1) */
2681 96 : path.add_event (t.get_line_and_columns (6, 25, 35), nullptr, 0,
2682 : "allocated here");
2683 :
2684 : /* (2) */
2685 96 : path.add_event (t.get_line_and_columns (8, 13, 14, 17), nullptr, 0,
2686 : "following %qs branch (when %qs)...",
2687 : "true", "i <= 254");
2688 96 : path.connect_to_next_event ();
2689 :
2690 : /* (3) */
2691 96 : path.add_event (t.get_line_and_columns (9, 5, 15, 17), nullptr, 0,
2692 : "...to here");
2693 :
2694 : /* (4) */
2695 96 : path.add_event (t.get_line_and_columns (8, 13, 14, 17), nullptr, 0,
2696 : "following %qs branch (when %qs)...",
2697 : "true", "i <= 254");
2698 96 : path.connect_to_next_event ();
2699 :
2700 : /* (5) */
2701 96 : path.add_event (t.get_line_and_columns (9, 5, 15, 17), nullptr, 0,
2702 : "...to here");
2703 :
2704 96 : if (!path_events_have_column_data_p (path))
2705 40 : return;
2706 :
2707 56 : {
2708 56 : selftest::test_context dc;
2709 56 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
2710 56 : dc.show_event_links (true);
2711 56 : dc.show_line_numbers (true);
2712 56 : text_sink text_output (dc);
2713 56 : path_print_policy policy (text_output);
2714 56 : path_summary summary (policy, *event_pp, path, true);
2715 56 : print_path_summary_as_text (summary, text_output, false);
2716 56 : ASSERT_STREQ
2717 : (" events 1-3\n"
2718 : "FILENAME:6:25:\n"
2719 : " 6 | char *buffer = (char*)malloc(256);\n"
2720 : " | ^~~~~~~~~~~\n"
2721 : " | |\n"
2722 : " | (1) allocated here\n"
2723 : " 7 | \n"
2724 : " 8 | for (i=0; i<255; i++) {\n"
2725 : " | ~~~~~ \n"
2726 : " | |\n"
2727 : " | (2) following `true' branch (when `i <= 254')... ->-+\n"
2728 : " | |\n"
2729 : " | |\n"
2730 : " |+-----------------------------------------------------------------+\n"
2731 : " 9 || buffer[i] = c;\n"
2732 : " || ~~~~~~~~~~~~~ \n"
2733 : " || |\n"
2734 : " |+------------->(3) ...to here\n"
2735 : " events 4-5\n"
2736 : " 8 | for (i=0; i<255; i++) {\n"
2737 : " | ~^~~~\n"
2738 : " | |\n"
2739 : " | (4) following `true' branch (when `i <= 254')... ->-+\n"
2740 : " | |\n"
2741 : " | |\n"
2742 : " |+-----------------------------------------------------------------+\n"
2743 : " 9 || buffer[i] = c;\n"
2744 : " || ~~~~~~~~~~~~~\n"
2745 : " || |\n"
2746 : " |+------------->(5) ...to here\n",
2747 : pp_formatted_text (text_output.get_printer ()));
2748 56 : }
2749 192 : }
2750 :
2751 : static void
2752 96 : control_flow_tests (const line_table_case &case_)
2753 : {
2754 96 : pretty_printer pp;
2755 96 : pp_show_color (&pp) = false;
2756 :
2757 96 : test_control_flow_1 (case_, &pp);
2758 96 : test_control_flow_2 (case_, &pp);
2759 96 : test_control_flow_3 (case_, &pp);
2760 96 : test_control_flow_4 (case_, &pp);
2761 96 : test_control_flow_5 (case_, &pp);
2762 96 : test_control_flow_6 (case_, &pp);
2763 96 : }
2764 :
2765 : } // namespace diagnostics::paths::selftest
2766 : } // namespace diagnostics::paths
2767 :
2768 : namespace selftest { // diagnostics::selftest
2769 :
2770 : /* Run all of the selftests within this file. */
2771 :
2772 : void
2773 4 : paths_output_cc_tests ()
2774 : {
2775 4 : pretty_printer pp;
2776 4 : pp_show_color (&pp) = false;
2777 :
2778 4 : ::selftest::auto_fix_quotes fix_quotes;
2779 4 : diagnostics::paths::selftest::test_empty_path (&pp);
2780 4 : diagnostics::paths::selftest::test_intraprocedural_path (&pp);
2781 4 : diagnostics::paths::selftest::test_interprocedural_path_1 (&pp);
2782 4 : diagnostics::paths::selftest::test_interprocedural_path_2 (&pp);
2783 4 : diagnostics::paths::selftest::test_recursion (&pp);
2784 4 : for_each_line_table_case (diagnostics::paths::selftest::control_flow_tests);
2785 4 : }
2786 :
2787 : } // namespace diagnostics::selftest
2788 : } // namespace diagnostics
2789 :
2790 : #if __GNUC__ >= 10
2791 : # pragma GCC diagnostic pop
2792 : #endif
2793 :
2794 : #endif /* #if CHECKING_P */
|