Line data Source code
1 : /* Subclasses of diagnostics::paths::event for analyzer diagnostics.
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
8 : under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3, or (at your option)
10 : any later version.
11 :
12 : GCC is distributed in the hope that it will be useful, but
13 : WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : General Public License 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 "analyzer/common.h"
22 :
23 : #include "gimple-pretty-print.h"
24 : #include "sbitmap.h"
25 : #include "ordered-hash-map.h"
26 : #include "fold-const.h"
27 : #include "gimple-iterator.h"
28 : #include "inlining-iterator.h"
29 : #include "tree-logical-location.h"
30 : #include "diagnostics/sarif-sink.h"
31 : #include "diagnostics/state-graphs.h"
32 : #include "custom-sarif-properties/state-graphs.h"
33 :
34 : #include "analyzer/analyzer-logging.h"
35 : #include "analyzer/sm.h"
36 : #include "analyzer/call-string.h"
37 : #include "analyzer/program-point.h"
38 : #include "analyzer/store.h"
39 : #include "analyzer/region-model.h"
40 : #include "analyzer/program-state.h"
41 : #include "analyzer/checker-path.h"
42 : #include "analyzer/supergraph.h"
43 : #include "analyzer/pending-diagnostic.h"
44 : #include "analyzer/diagnostic-manager.h"
45 : #include "analyzer/constraint-manager.h"
46 : #include "analyzer/checker-event.h"
47 : #include "analyzer/exploded-graph.h"
48 :
49 : #if ENABLE_ANALYZER
50 :
51 : namespace ana {
52 :
53 : /* Get a string for EK. */
54 :
55 : const char *
56 192 : event_kind_to_string (enum event_kind ek)
57 : {
58 192 : switch (ek)
59 : {
60 0 : default:
61 0 : gcc_unreachable ();
62 : case event_kind::debug:
63 : return "debug";
64 4 : case event_kind::custom:
65 4 : return "custom";
66 8 : case event_kind::stmt:
67 8 : return "stmt";
68 18 : case event_kind::region_creation:
69 18 : return "region_creation";
70 23 : case event_kind::function_entry:
71 23 : return "function_entry";
72 26 : case event_kind::state_change:
73 26 : return "state_change";
74 19 : case event_kind::start_cfg_edge:
75 19 : return "start_cfg_edge";
76 19 : case event_kind::end_cfg_edge:
77 19 : return "end_cfg_edge";
78 6 : case event_kind::catch_:
79 6 : return "catch";
80 13 : case event_kind::call_:
81 13 : return "call";
82 3 : case event_kind::return_:
83 3 : return "return";
84 0 : case event_kind::start_consolidated_cfg_edges:
85 0 : return "start_consolidated_cfg_edges";
86 0 : case event_kind::end_consolidated_cfg_edges:
87 0 : return "end_consolidated_cfg_edges";
88 1 : case event_kind::inlined_call:
89 1 : return "inlined_call";
90 1 : case event_kind::setjmp_:
91 1 : return "setjmp";
92 1 : case event_kind::rewind_from_longjmp:
93 1 : return "rewind_from_longjmp";
94 1 : case event_kind::rewind_to_setjmp:
95 1 : return "rewind_to_setjmp";
96 6 : case event_kind::throw_:
97 6 : return "throw";
98 3 : case event_kind::unwind:
99 3 : return "unwind";
100 40 : case event_kind::warning:
101 40 : return "warning";
102 : }
103 : }
104 :
105 : /* class checker_event : public diagnostics::paths::event. */
106 :
107 : /* checker_event's ctor. */
108 :
109 36566 : checker_event::checker_event (enum event_kind kind,
110 36566 : const event_loc_info &loc_info)
111 36566 : : m_path (nullptr),
112 36566 : m_kind (kind), m_loc (loc_info.m_loc),
113 36566 : m_original_fndecl (loc_info.m_fndecl),
114 36566 : m_effective_fndecl (loc_info.m_fndecl),
115 36566 : m_original_depth (loc_info.m_depth),
116 36566 : m_effective_depth (loc_info.m_depth),
117 36566 : m_pending_diagnostic (nullptr), m_emission_id (),
118 : m_logical_loc
119 36566 : (tree_logical_location_manager::key_from_tree (loc_info.m_fndecl))
120 : {
121 : /* Update effective fndecl and depth if inlining has been recorded. */
122 36566 : if (flag_analyzer_undo_inlining)
123 : {
124 36554 : inlining_info info (m_loc);
125 36554 : if (info.get_inner_fndecl ())
126 : {
127 29451 : m_effective_fndecl = info.get_inner_fndecl ();
128 29451 : m_effective_depth += info.get_extra_frames ();
129 29451 : m_logical_loc
130 29451 : = tree_logical_location_manager::key_from_tree (m_effective_fndecl);
131 : }
132 : }
133 36566 : }
134 :
135 : /* No-op implementation of diagnostics::paths::event::get_meaning vfunc for
136 : checker_event: checker events have no meaning by default. */
137 :
138 : diagnostics::paths::event::meaning
139 56 : checker_event::get_meaning () const
140 : {
141 56 : return diagnostics::paths::event::meaning ();
142 : }
143 :
144 : /* Implementation of diagnostics::paths::event::maybe_add_sarif_properties
145 : for checker_event. */
146 :
147 : void
148 155 : checker_event::
149 : maybe_add_sarif_properties (diagnostics::sarif_builder &builder,
150 : diagnostics::sarif_object &thread_flow_loc_obj) const
151 : {
152 155 : auto &props = thread_flow_loc_obj.get_or_create_properties ();
153 : #define PROPERTY_PREFIX "gcc/analyzer/checker_event/"
154 310 : props.set (PROPERTY_PREFIX "emission_id",
155 155 : diagnostic_event_id_to_json (m_emission_id));
156 155 : props.set_string (PROPERTY_PREFIX "kind", event_kind_to_string (m_kind));
157 :
158 155 : if (m_original_fndecl != m_effective_fndecl)
159 0 : props.set_logical_location
160 0 : (PROPERTY_PREFIX "original_fndecl",
161 : builder,
162 0 : tree_logical_location_manager::key_from_tree (m_original_fndecl));
163 :
164 155 : if (m_original_depth != m_effective_depth)
165 0 : props.set_integer (PROPERTY_PREFIX "original_depth", m_original_depth);
166 : #undef PROPERTY_PREFIX
167 155 : }
168 :
169 : /* Dump this event to PP (for debugging/logging purposes). */
170 :
171 : void
172 37 : checker_event::dump (pretty_printer *pp) const
173 : {
174 37 : pp_character (pp, '"');
175 37 : print_desc (*pp);
176 37 : pp_printf (pp, "\" (depth %i", m_effective_depth);
177 :
178 37 : if (m_effective_depth != m_original_depth)
179 10 : pp_printf (pp, " corrected from %i",
180 : m_original_depth);
181 37 : if (m_effective_fndecl)
182 : {
183 37 : pp_printf (pp, ", fndecl %qE", m_effective_fndecl);
184 37 : if (m_effective_fndecl != m_original_fndecl)
185 10 : pp_printf (pp, " corrected from %qE", m_original_fndecl);
186 : }
187 37 : pp_printf (pp, ", m_loc=%llx)",
188 37 : (unsigned long long) get_location ());
189 37 : }
190 :
191 : /* Dump this event to stderr (for debugging/logging purposes). */
192 :
193 : DEBUG_FUNCTION void
194 0 : checker_event::debug () const
195 : {
196 0 : tree_dump_pretty_printer pp (stderr);
197 0 : dump (&pp);
198 0 : pp_newline (&pp);
199 0 : }
200 :
201 : /* Hook for being notified when this event has its final id EMISSION_ID
202 : and is about to emitted for PD.
203 :
204 : Base implementation of checker_event::prepare_for_emission vfunc;
205 : subclasses that override this should chain up to it.
206 :
207 : Record PD and EMISSION_ID, and call the print_desc vfunc, so that any
208 : side-effects of the call to print_desc take place before
209 : pending_diagnostic::emit is called.
210 :
211 : For example, state_change_event::print_desc can call
212 : pending_diagnostic::describe_state_change; free_of_non_heap can use this
213 : to tweak the message (TODO: would be neater to simply capture the
214 : pertinent data within the sm-state). */
215 :
216 : void
217 16707 : checker_event::prepare_for_emission (checker_path *path,
218 : pending_diagnostic *pd,
219 : diagnostics::paths::event_id_t emission_id)
220 : {
221 16707 : m_path = path;
222 16707 : m_pending_diagnostic = pd;
223 16707 : m_emission_id = emission_id;
224 :
225 16707 : auto pp = global_dc->clone_printer ();
226 16707 : print_desc (*pp.get ());
227 16707 : }
228 :
229 : std::unique_ptr<diagnostics::digraphs::digraph>
230 28 : checker_event::maybe_make_diagnostic_state_graph (bool debug) const
231 : {
232 28 : const program_state *state = get_program_state ();
233 28 : if (!state)
234 0 : return nullptr;
235 :
236 28 : gcc_assert (m_path);
237 28 : const extrinsic_state &ext_state = m_path->get_ext_state ();
238 :
239 28 : auto result = state->make_diagnostic_state_graph (ext_state);
240 :
241 28 : if (debug)
242 : {
243 27 : pretty_printer pp;
244 27 : text_art::theme *theme = global_dc->get_diagram_theme ();
245 27 : text_art::dump_to_pp (*state, theme, &pp);
246 27 : const json::string_property program_state_property
247 : (custom_sarif_properties::state_graphs::graph::prefix,
248 27 : "analyzer/program_state/");
249 27 : result->set_property (program_state_property,
250 : pp_formatted_text (&pp));
251 27 : }
252 :
253 28 : return result;
254 28 : }
255 :
256 : /* class debug_event : public checker_event. */
257 :
258 : /* Implementation of diagnostics::paths::event::print_desc vfunc for
259 : debug_event.
260 : Use the saved string as the event's description. */
261 :
262 : void
263 0 : debug_event::print_desc (pretty_printer &pp) const
264 : {
265 0 : pp_string (&pp, m_desc);
266 0 : }
267 :
268 : /* class precanned_custom_event : public custom_event. */
269 :
270 : /* Implementation of diagnostics::paths::event::print_desc vfunc for
271 : precanned_custom_event.
272 : Use the saved string as the event's description. */
273 :
274 : void
275 40 : precanned_custom_event::print_desc (pretty_printer &pp) const
276 : {
277 40 : pp_string (&pp, m_desc);
278 40 : }
279 :
280 : /* class statement_event : public checker_event. */
281 :
282 : /* statement_event's ctor. */
283 :
284 13613 : statement_event::statement_event (const gimple *stmt, tree fndecl, int depth,
285 13613 : const program_state &dst_state)
286 : : checker_event (event_kind::stmt,
287 13613 : event_loc_info (gimple_location (stmt), fndecl, depth)),
288 13613 : m_stmt (stmt),
289 13613 : m_dst_state (dst_state)
290 : {
291 13613 : }
292 :
293 : /* Implementation of diagnostics::paths::event::print_desc vfunc for
294 : statement_event.
295 : Use the statement's dump form as the event's description. */
296 :
297 : void
298 12 : statement_event::print_desc (pretty_printer &pp) const
299 : {
300 12 : pp_string (&pp, "stmt: ");
301 12 : pp_gimple_stmt_1 (&pp, m_stmt, 0, (dump_flags_t)0);
302 12 : }
303 :
304 : /* class region_creation_event : public checker_event. */
305 :
306 1571 : region_creation_event::region_creation_event (const event_loc_info &loc_info)
307 1571 : : checker_event (event_kind::region_creation, loc_info)
308 : {
309 1571 : }
310 :
311 : /* The various region_creation_event subclasses' print_desc
312 : implementations. */
313 :
314 : void
315 1208 : region_creation_event_memory_space::print_desc (pretty_printer &pp) const
316 : {
317 1208 : switch (m_mem_space)
318 : {
319 28 : default:
320 28 : pp_string (&pp, "region created here");
321 28 : return;
322 1082 : case MEMSPACE_STACK:
323 1082 : pp_string (&pp, "region created on stack here");
324 1082 : return;
325 98 : case MEMSPACE_HEAP:
326 98 : pp_string (&pp, "region created on heap here");
327 98 : return;
328 : }
329 : }
330 :
331 : void
332 1618 : region_creation_event_capacity::print_desc (pretty_printer &pp) const
333 : {
334 1618 : gcc_assert (m_capacity);
335 1618 : if (TREE_CODE (m_capacity) == INTEGER_CST)
336 : {
337 1422 : unsigned HOST_WIDE_INT hwi = tree_to_uhwi (m_capacity);
338 1422 : return pp_printf_n (&pp,
339 : hwi,
340 : "capacity: %wu byte",
341 : "capacity: %wu bytes",
342 1422 : hwi);
343 : }
344 : else
345 196 : return pp_printf (&pp, "capacity: %qE bytes", m_capacity);
346 : }
347 :
348 : void
349 214 : region_creation_event_allocation_size::print_desc (pretty_printer &pp) const
350 : {
351 214 : if (m_capacity)
352 : {
353 206 : if (TREE_CODE (m_capacity) == INTEGER_CST)
354 118 : pp_printf_n (&pp,
355 : tree_to_uhwi (m_capacity),
356 : "allocated %E byte here",
357 : "allocated %E bytes here",
358 : m_capacity);
359 : else
360 88 : pp_printf (&pp,
361 : "allocated %qE bytes here",
362 : m_capacity);
363 : }
364 : else
365 8 : pp_printf (&pp, "allocated here");
366 214 : }
367 :
368 : void
369 0 : region_creation_event_debug::print_desc (pretty_printer &pp) const
370 : {
371 0 : pp_string (&pp, "region creation: ");
372 0 : m_reg->dump_to_pp (&pp, true);
373 0 : if (m_capacity)
374 0 : pp_printf (&pp, " capacity: %qE", m_capacity);
375 0 : }
376 :
377 : /* class function_entry_event : public checker_event. */
378 :
379 5104 : function_entry_event::function_entry_event (const program_point &dst_point,
380 5104 : const program_state &state)
381 : : checker_event (event_kind::function_entry,
382 5104 : event_loc_info (dst_point.get_location (),
383 : dst_point.get_fndecl (),
384 15312 : dst_point.get_stack_depth ())),
385 10208 : m_state (state)
386 : {
387 5104 : }
388 :
389 : /* Implementation of diagnostics::paths::event::print_desc vfunc for
390 : function_entry_event.
391 :
392 : Use a string such as "entry to 'foo'" as the event's description. */
393 :
394 : void
395 2719 : function_entry_event::print_desc (pretty_printer &pp) const
396 : {
397 2719 : pp_printf (&pp, "entry to %qE", m_effective_fndecl);
398 2719 : }
399 :
400 : /* Implementation of diagnostics::paths::event::get_meaning vfunc for
401 : function entry. */
402 :
403 : diagnostics::paths::event::meaning
404 220 : function_entry_event::get_meaning () const
405 : {
406 220 : return meaning (verb::enter, noun::function);
407 : }
408 :
409 : /* class state_change_event : public checker_event. */
410 :
411 : /* state_change_event's ctor. */
412 :
413 3772 : state_change_event::state_change_event (const event_loc_info &loc_info,
414 : const gimple *stmt,
415 : const state_machine &sm,
416 : const svalue *sval,
417 : state_machine::state_t from,
418 : state_machine::state_t to,
419 : const svalue *origin,
420 : const program_state &dst_state,
421 3772 : const exploded_node *enode)
422 : : checker_event (event_kind::state_change, loc_info),
423 3772 : m_stmt (stmt),
424 3772 : m_sm (sm),
425 3772 : m_sval (sval), m_from (from), m_to (to),
426 3772 : m_origin (origin),
427 3772 : m_dst_state (dst_state),
428 3772 : m_enode (enode)
429 : {
430 3772 : }
431 :
432 : /* Implementation of diagnostics::paths::event::print_desc vfunc for
433 : state_change_event.
434 :
435 : Attempt to generate a nicer human-readable description.
436 : For greatest precision-of-wording, give the pending diagnostic
437 : a chance to describe this state change (in terms of the
438 : diagnostic).
439 : Note that we only have a pending_diagnostic set on the event once
440 : the diagnostic is about to being emitted, so the description for
441 : an event can change. */
442 :
443 : void
444 4204 : state_change_event::print_desc (pretty_printer &pp) const
445 : {
446 4204 : if (m_pending_diagnostic)
447 : {
448 4204 : region_model *model = m_dst_state.m_region_model;
449 4204 : tree var = model->get_representative_tree (m_sval);
450 4204 : tree origin = model->get_representative_tree (m_origin);
451 4204 : evdesc::state_change evd (var, origin,
452 4204 : m_from, m_to, m_emission_id, *this);
453 4204 : if (m_pending_diagnostic->describe_state_change (pp, evd))
454 : {
455 4198 : if (flag_analyzer_verbose_state_changes)
456 : {
457 : /* Append debugging information about this event. */
458 :
459 144 : if (var)
460 136 : pp_printf (&pp, " (state of %qE: ", var);
461 : else
462 8 : pp_string (&pp, " (state: ");
463 :
464 144 : pp_printf (&pp, "%qs -> %qs, ",
465 144 : m_from->get_name (),
466 144 : m_to->get_name ());
467 :
468 144 : if (m_origin)
469 0 : pp_printf (&pp, "origin: %qE", origin);
470 : else
471 144 : pp_string (&pp, "NULL origin");
472 :
473 : /* Get any "meaning" of event. */
474 144 : diagnostics::paths::event::meaning meaning = get_meaning ();
475 144 : pp_string (&pp, ", meaning: ");
476 144 : meaning.dump_to_pp (&pp);
477 144 : pp_string (&pp, ")");
478 : }
479 4198 : return;
480 : }
481 : }
482 :
483 : /* Fallback description. */
484 6 : if (m_sval)
485 : {
486 6 : label_text sval_desc = m_sval->get_desc ();
487 6 : pp_printf (&pp,
488 : "state of %qs: %qs -> %qs",
489 : sval_desc.get (),
490 6 : m_from->get_name (),
491 6 : m_to->get_name ());
492 6 : if (m_origin)
493 : {
494 0 : label_text origin_desc = m_origin->get_desc ();
495 0 : pp_printf (&pp, " (origin: %qs)",
496 : origin_desc.get ());
497 0 : }
498 : else
499 6 : pp_string (&pp, " (NULL origin)");
500 6 : }
501 : else
502 : {
503 0 : gcc_assert (m_origin == nullptr);
504 0 : pp_printf (&pp,
505 : "global state: %qs -> %qs",
506 0 : m_from->get_name (),
507 0 : m_to->get_name ());
508 : }
509 : }
510 :
511 : /* Implementation of diagnostics::paths::event::get_meaning vfunc for
512 : state change events: delegate to the pending_diagnostic to
513 : get any meaning. */
514 :
515 : diagnostics::paths::event::meaning
516 336 : state_change_event::get_meaning () const
517 : {
518 336 : if (m_pending_diagnostic)
519 : {
520 336 : region_model *model = m_dst_state.m_region_model;
521 336 : tree var = model->get_representative_tree (m_sval);
522 336 : tree origin = model->get_representative_tree (m_origin);
523 336 : evdesc::state_change evd (var, origin,
524 336 : m_from, m_to, m_emission_id, *this);
525 336 : return m_pending_diagnostic->get_meaning_for_state_change (evd);
526 : }
527 : else
528 0 : return meaning ();
529 : }
530 :
531 : /* class superedge_event : public checker_event. */
532 :
533 : /* Implementation of diagnostics::paths::event::maybe_add_sarif_properties
534 : for superedge_event. */
535 :
536 : void
537 49 : superedge_event::
538 : maybe_add_sarif_properties (diagnostics::sarif_builder &builder,
539 : diagnostics::sarif_object &thread_flow_loc_obj)
540 : const
541 : {
542 49 : checker_event::maybe_add_sarif_properties (builder, thread_flow_loc_obj);
543 49 : auto &props = thread_flow_loc_obj.get_or_create_properties ();
544 : #define PROPERTY_PREFIX "gcc/analyzer/superedge_event/"
545 49 : if (m_sedge)
546 49 : props.set (PROPERTY_PREFIX "superedge", m_sedge->to_json ());
547 : #undef PROPERTY_PREFIX
548 49 : }
549 :
550 : /* Determine if this event should be filtered at the given verbosity
551 : level. */
552 :
553 : bool
554 2607 : superedge_event::should_filter_p (int verbosity) const
555 : {
556 2607 : if (m_sedge->get_any_cfg_edge ())
557 : {
558 2607 : if (verbosity < 2)
559 : return true;
560 :
561 2574 : if (verbosity < 4)
562 : {
563 : /* Filter events with empty descriptions. This ought to filter
564 : FALLTHRU, but retain true/false/switch edges. */
565 2574 : auto pp = global_dc->clone_printer ();
566 2574 : print_desc (*pp.get ());
567 2574 : if (pp_formatted_text (pp.get ()) [0] == '\0')
568 3 : return true;
569 2574 : }
570 : }
571 : return false;
572 : }
573 :
574 : const program_state *
575 12 : superedge_event::get_program_state () const
576 : {
577 12 : return &m_eedge.m_dest->get_state ();
578 : }
579 :
580 : const call_and_return_op *
581 1193 : superedge_event::get_call_and_return_op () const
582 : {
583 1193 : if (m_sedge)
584 1193 : if (auto base_op = m_sedge->get_op ())
585 1193 : return base_op->dyn_cast_call_and_return_op ();
586 : return nullptr;
587 : }
588 :
589 : /* superedge_event's ctor. */
590 :
591 6800 : superedge_event::superedge_event (enum event_kind kind,
592 : const exploded_edge &eedge,
593 6800 : const event_loc_info &loc_info)
594 : : checker_event (kind, loc_info),
595 6800 : m_eedge (eedge), m_sedge (eedge.m_sedge)
596 : {
597 6800 : gcc_assert (m_sedge);
598 6800 : }
599 :
600 : /* class cfg_edge_event : public superedge_event. */
601 :
602 : /* cfg_edge_event's ctor. */
603 :
604 5607 : cfg_edge_event::cfg_edge_event (enum event_kind kind,
605 : const exploded_edge &eedge,
606 : const event_loc_info &loc_info,
607 5607 : const control_flow_op *op)
608 : : superedge_event (kind, eedge, loc_info),
609 5607 : m_op (op)
610 : {
611 5607 : }
612 :
613 : /* Implementation of diagnostics::paths::event::get_meaning vfunc for
614 : CFG edge events. */
615 :
616 : diagnostics::paths::event::meaning
617 292 : cfg_edge_event::get_meaning () const
618 : {
619 292 : if (::edge e = get_cfg_edge ())
620 : {
621 292 : if (e->flags & EDGE_TRUE_VALUE)
622 124 : return meaning (verb::branch, property::true_);
623 168 : else if (e->flags & EDGE_FALSE_VALUE)
624 128 : return meaning (verb::branch, property::false_);
625 : }
626 40 : return meaning ();
627 : }
628 :
629 : ::edge
630 799 : cfg_edge_event::get_cfg_edge () const
631 : {
632 799 : return m_sedge->get_any_cfg_edge ();
633 : }
634 :
635 : /* If this event is for a conditional, write the sense of the
636 : conditional to *OUT and return true.
637 : Otherwise return false. */
638 :
639 : bool
640 507 : cfg_edge_event::maybe_get_edge_sense (bool *out) const
641 : {
642 507 : if (::edge e = get_cfg_edge ())
643 : {
644 507 : if (e->flags & EDGE_TRUE_VALUE)
645 : {
646 256 : *out = true;
647 256 : return true;
648 : }
649 251 : else if (e->flags & EDGE_FALSE_VALUE)
650 : {
651 251 : *out = false;
652 251 : return true;
653 : }
654 : }
655 : return false;
656 : }
657 :
658 : /* class start_cfg_edge_event : public cfg_edge_event. */
659 :
660 : /* Implementation of diagnostics::paths::event::print_desc vfunc for
661 : start_cfg_edge_event.
662 :
663 : If -fanalyzer-verbose-edges, then generate low-level descriptions, such
664 : as
665 : "taking 'true' edge SN:7 -> SN:8".
666 :
667 : Otherwise, generate strings using the label of the underlying CFG if
668 : any, such as:
669 : "following 'true' branch..." or
670 : "following 'case 3' branch..."
671 : "following 'default' branch..."
672 :
673 : For conditionals, attempt to supply a description of the condition that
674 : holds, such as:
675 : "following 'false' branch (when 'ptr' is non-NULL)..."
676 :
677 : Failing that, print nothing (which will lead to this event
678 : being filtered). */
679 :
680 : void
681 7070 : start_cfg_edge_event::print_desc (pretty_printer &pp) const
682 : {
683 7070 : bool user_facing = !flag_analyzer_verbose_edges;
684 7070 : label_text edge_desc (m_sedge->get_description (user_facing));
685 7070 : if (user_facing)
686 : {
687 7070 : if (edge_desc.get ()
688 7070 : && strlen (edge_desc.get ()) > 0
689 14137 : && m_op)
690 : {
691 7067 : label_text cond_desc
692 7067 : = m_op->maybe_describe_condition (pp_show_color (&pp));
693 7067 : label_text result;
694 7067 : if (cond_desc.get ())
695 3954 : pp_printf (&pp,
696 : "following %qs branch (%s)...",
697 : edge_desc.get (), cond_desc.get ());
698 : else
699 3113 : pp_printf (&pp,
700 : "following %qs branch...",
701 : edge_desc.get ());
702 10901 : }
703 : }
704 : else
705 : {
706 0 : if (strlen (edge_desc.get ()) > 0)
707 0 : return pp_printf (&pp,
708 : "taking %qs edge SN:%i -> SN:%i",
709 : edge_desc.get (),
710 0 : m_sedge->m_src->m_id,
711 0 : m_sedge->m_dest->m_id);
712 : else
713 0 : return pp_printf (&pp,
714 : "taking edge SN:%i -> SN:%i",
715 0 : m_sedge->m_src->m_id,
716 0 : m_sedge->m_dest->m_id);
717 : }
718 7070 : }
719 :
720 : /* class catch_cfg_edge_event : public cfg_edge_event. */
721 :
722 : diagnostics::paths::event::meaning
723 6 : catch_cfg_edge_event::get_meaning () const
724 : {
725 6 : return meaning (verb::catch_);
726 : }
727 :
728 : /* class call_event : public superedge_event. */
729 :
730 : /* call_event's ctor. */
731 :
732 1193 : call_event::call_event (const exploded_edge &eedge,
733 1193 : const event_loc_info &loc_info)
734 1193 : : superedge_event (event_kind::call_, eedge, loc_info)
735 : {
736 1193 : m_src_snode = eedge.m_src->get_supernode ();
737 1193 : m_dest_snode = eedge.m_dest->get_supernode ();
738 1193 : }
739 :
740 : /* Implementation of diagnostics::paths::event::print_desc vfunc for
741 : call_event.
742 :
743 : If this call event passes critical state for an sm-based warning,
744 : allow the diagnostic to generate a precise description, such as:
745 :
746 : "passing freed pointer 'ptr' in call to 'foo' from 'bar'"
747 :
748 : Otherwise, generate a description of the form
749 : "calling 'foo' from 'bar'". */
750 :
751 : void
752 1707 : call_event::print_desc (pretty_printer &pp) const
753 : {
754 1707 : if (m_critical_state.m_state && m_pending_diagnostic)
755 : {
756 388 : gcc_assert (m_critical_state.m_var);
757 388 : tree var = fixup_tree_for_diagnostic (m_critical_state.m_var);
758 388 : evdesc::call_with_state evd (m_src_snode->m_fun->decl,
759 388 : m_dest_snode->m_fun->decl,
760 : var,
761 388 : m_critical_state.m_state);
762 388 : if (m_pending_diagnostic->describe_call_with_state (pp, evd))
763 180 : return;
764 : }
765 :
766 1527 : pp_printf (&pp,
767 : "calling %qE from %qE",
768 : get_callee_fndecl (),
769 : get_caller_fndecl ());
770 : }
771 :
772 : /* Implementation of diagnostics::paths::event::get_meaning vfunc for
773 : function call events. */
774 :
775 : diagnostics::paths::event::meaning
776 136 : call_event::get_meaning () const
777 : {
778 136 : return meaning (verb::call, noun::function);
779 : }
780 :
781 : /* Override of checker_event::is_call_p for calls. */
782 :
783 : bool
784 1919 : call_event::is_call_p () const
785 : {
786 1919 : return true;
787 : }
788 :
789 : tree
790 1595 : call_event::get_caller_fndecl () const
791 : {
792 1595 : return m_src_snode->m_fun->decl;
793 : }
794 :
795 : tree
796 1595 : call_event::get_callee_fndecl () const
797 : {
798 1595 : return m_dest_snode->m_fun->decl;
799 : }
800 :
801 : const program_state *
802 3 : call_event::get_program_state () const
803 : {
804 : /* Use the state at the source (at the caller),
805 : rather than the one at the dest, which has a frame for the callee. */
806 3 : return &m_eedge.m_src->get_state ();
807 : }
808 :
809 : /* class return_event : public checker_event. */
810 :
811 : /* return_event's ctor. */
812 :
813 601 : return_event::return_event (const exploded_edge &eedge,
814 601 : const event_loc_info &loc_info)
815 : : checker_event (event_kind::return_, loc_info),
816 601 : m_eedge (eedge)
817 : {
818 601 : m_src_snode = eedge.m_src->get_supernode ();
819 601 : m_dest_snode = eedge.m_dest->get_supernode ();
820 601 : m_call_and_return_op
821 601 : = eedge.m_src->get_point ().get_call_string ().get_top_of_stack ().m_call_op;
822 601 : }
823 :
824 : /* Implementation of diagnostics::paths::event::print_desc vfunc for
825 : return_event.
826 :
827 : If this return event returns critical state for an sm-based warning,
828 : allow the diagnostic to generate a precise description, such as:
829 :
830 : "possible of NULL to 'foo' from 'bar'"
831 :
832 : Otherwise, generate a description of the form
833 : "returning to 'foo' from 'bar'. */
834 :
835 : void
836 576 : return_event::print_desc (pretty_printer &pp) const
837 : {
838 : /* For greatest precision-of-wording, if this is returning the
839 : state involved in the pending diagnostic, give the pending
840 : diagnostic a chance to describe this return (in terms of
841 : itself). */
842 576 : if (m_critical_state.m_state && m_pending_diagnostic)
843 : {
844 133 : evdesc::return_of_state evd (m_dest_snode->m_fun->decl,
845 133 : m_src_snode->m_fun->decl,
846 133 : m_critical_state.m_state);
847 133 : if (m_pending_diagnostic->describe_return_of_state (pp, evd))
848 50 : return;
849 : }
850 526 : pp_printf (&pp,
851 : "returning to %qE from %qE",
852 526 : m_dest_snode->m_fun->decl,
853 526 : m_src_snode->m_fun->decl);
854 : }
855 :
856 : /* Implementation of diagnostics::paths::event::get_meaning vfunc for
857 : function return events. */
858 :
859 : diagnostics::paths::event::meaning
860 54 : return_event::get_meaning () const
861 : {
862 54 : return meaning (verb::return_, noun::function);
863 : }
864 :
865 : /* Override of checker_event::is_return_p for returns. */
866 :
867 : bool
868 601 : return_event::is_return_p () const
869 : {
870 601 : return true;
871 : }
872 :
873 : const program_state *
874 3 : return_event::get_program_state () const
875 : {
876 3 : return &m_eedge.m_dest->get_state ();
877 : }
878 :
879 : /* class start_consolidated_cfg_edges_event : public checker_event. */
880 :
881 : void
882 200 : start_consolidated_cfg_edges_event::print_desc (pretty_printer &pp) const
883 : {
884 200 : pp_printf (&pp,
885 : "following %qs branch...",
886 200 : m_edge_sense ? "true" : "false");
887 200 : }
888 :
889 : /* Implementation of diagnostics::paths::event::get_meaning vfunc for
890 : start_consolidated_cfg_edges_event. */
891 :
892 : diagnostics::paths::event::meaning
893 0 : start_consolidated_cfg_edges_event::get_meaning () const
894 : {
895 0 : return meaning (verb::branch,
896 0 : (m_edge_sense ? property::true_ : property::false_));
897 : }
898 :
899 : /* class inlined_call_event : public checker_event. */
900 :
901 : void
902 326 : inlined_call_event::print_desc (pretty_printer &pp) const
903 : {
904 326 : pp_printf (&pp,
905 : "inlined call to %qE from %qE",
906 326 : m_apparent_callee_fndecl,
907 326 : m_apparent_caller_fndecl);
908 326 : }
909 :
910 : /* Implementation of diagnostics::paths::event::get_meaning vfunc for
911 : reconstructed inlined function calls. */
912 :
913 : diagnostics::paths::event::meaning
914 72 : inlined_call_event::get_meaning () const
915 : {
916 72 : return meaning (verb::call, noun::function);
917 : }
918 :
919 : /* class setjmp_event : public checker_event. */
920 :
921 : /* Implementation of diagnostics::paths::event::print_desc vfunc for
922 : setjmp_event. */
923 :
924 : void
925 41 : setjmp_event::print_desc (pretty_printer &pp) const
926 : {
927 41 : pp_printf (&pp,
928 : "%qs called here",
929 : get_user_facing_name (m_setjmp_call));
930 41 : }
931 :
932 : diagnostics::paths::event::meaning
933 17 : setjmp_event::get_meaning () const
934 : {
935 17 : return meaning (verb::setjmp_);
936 : }
937 :
938 : /* Implementation of checker_event::prepare_for_emission vfunc for setjmp_event.
939 :
940 : Record this setjmp's event ID into the path, so that rewind events can
941 : use it. */
942 :
943 : void
944 20 : setjmp_event::prepare_for_emission (checker_path *path,
945 : pending_diagnostic *pd,
946 : diagnostics::paths::event_id_t emission_id)
947 : {
948 20 : checker_event::prepare_for_emission (path, pd, emission_id);
949 20 : path->record_setjmp_event (m_enode, emission_id);
950 20 : }
951 :
952 : /* class rewind_event : public checker_event. */
953 :
954 : /* Get the fndecl containing the site of the longjmp call. */
955 :
956 : tree
957 93 : rewind_event::get_longjmp_caller () const
958 : {
959 93 : return m_eedge->m_src->get_function ()->decl;
960 : }
961 :
962 : /* Get the fndecl containing the site of the setjmp call. */
963 :
964 : tree
965 85 : rewind_event::get_setjmp_caller () const
966 : {
967 85 : return m_eedge->m_dest->get_function ()->decl;
968 : }
969 :
970 : diagnostics::paths::event::meaning
971 26 : rewind_event::get_meaning () const
972 : {
973 26 : return meaning (verb::longjmp_);
974 : }
975 :
976 : /* rewind_event's ctor. */
977 :
978 30 : rewind_event::rewind_event (const exploded_edge *eedge,
979 : enum event_kind kind,
980 : const event_loc_info &loc_info,
981 30 : const rewind_info_t *rewind_info)
982 : : checker_event (kind, loc_info),
983 30 : m_rewind_info (rewind_info),
984 30 : m_eedge (eedge)
985 : {
986 30 : gcc_assert (m_eedge->m_custom_info.get () == m_rewind_info);
987 30 : }
988 :
989 : /* class rewind_from_longjmp_event : public rewind_event. */
990 :
991 : /* Implementation of diagnostics::paths::event::print_desc vfunc for
992 : rewind_from_longjmp_event. */
993 :
994 : void
995 31 : rewind_from_longjmp_event::print_desc (pretty_printer &pp) const
996 : {
997 31 : const char *src_name
998 31 : = get_user_facing_name (m_rewind_info->get_longjmp_call ());
999 :
1000 31 : if (get_longjmp_caller () == get_setjmp_caller ())
1001 : /* Special-case: purely intraprocedural rewind. */
1002 8 : pp_printf (&pp,
1003 : "rewinding within %qE from %qs...",
1004 : get_longjmp_caller (),
1005 : src_name);
1006 : else
1007 23 : pp_printf (&pp,
1008 : "rewinding from %qs in %qE...",
1009 : src_name,
1010 : get_longjmp_caller ());
1011 31 : }
1012 :
1013 : /* class rewind_to_setjmp_event : public rewind_event. */
1014 :
1015 : /* Implementation of diagnostics::paths::event::print_desc vfunc for
1016 : rewind_to_setjmp_event. */
1017 :
1018 : void
1019 31 : rewind_to_setjmp_event::print_desc (pretty_printer &pp) const
1020 : {
1021 31 : const char *dst_name
1022 31 : = get_user_facing_name (m_rewind_info->get_setjmp_call ());
1023 :
1024 : /* If we can, identify the ID of the setjmp_event. */
1025 31 : if (m_original_setjmp_event_id.known_p ())
1026 : {
1027 16 : if (get_longjmp_caller () == get_setjmp_caller ())
1028 : /* Special-case: purely intraprocedural rewind. */
1029 4 : pp_printf (&pp,
1030 : "...to %qs (saved at %@)",
1031 : dst_name,
1032 : &m_original_setjmp_event_id);
1033 : else
1034 12 : pp_printf (&pp,
1035 : "...to %qs in %qE (saved at %@)",
1036 : dst_name,
1037 : get_setjmp_caller (),
1038 : &m_original_setjmp_event_id);
1039 : }
1040 : else
1041 : {
1042 15 : if (get_longjmp_caller () == get_setjmp_caller ())
1043 : /* Special-case: purely intraprocedural rewind. */
1044 4 : pp_printf (&pp,
1045 : "...to %qs",
1046 : dst_name);
1047 : else
1048 11 : pp_printf (&pp,
1049 : "...to %qs in %qE",
1050 : dst_name,
1051 : get_setjmp_caller ());
1052 : }
1053 31 : }
1054 :
1055 : /* Implementation of checker_event::prepare_for_emission vfunc for
1056 : rewind_to_setjmp_event.
1057 :
1058 : Attempt to look up the setjmp event ID that recorded the jmp_buf
1059 : for this rewind. */
1060 :
1061 : void
1062 15 : rewind_to_setjmp_event::prepare_for_emission (checker_path *path,
1063 : pending_diagnostic *pd,
1064 : diagnostics::paths::event_id_t emission_id)
1065 : {
1066 15 : checker_event::prepare_for_emission (path, pd, emission_id);
1067 15 : path->get_setjmp_event (m_rewind_info->get_enode_origin (),
1068 : &m_original_setjmp_event_id);
1069 15 : }
1070 :
1071 : /* class throw_event : public checker_event. */
1072 :
1073 : diagnostics::paths::event::meaning
1074 6 : throw_event::get_meaning () const
1075 : {
1076 6 : return meaning (verb::throw_);
1077 : }
1078 :
1079 : /* class explicit_throw_event : public throw_event. */
1080 : void
1081 150 : explicit_throw_event::print_desc (pretty_printer &pp) const
1082 : {
1083 150 : if (m_is_rethrow)
1084 : {
1085 30 : if (m_type)
1086 18 : pp_printf (&pp, "rethrowing exception of type %qT here...", m_type);
1087 : else
1088 12 : pp_printf (&pp, "rethrowing exception here...");
1089 : }
1090 : else
1091 : {
1092 120 : if (m_type)
1093 120 : pp_printf (&pp, "throwing exception of type %qT here...", m_type);
1094 : else
1095 0 : pp_printf (&pp, "throwing exception here...");
1096 : }
1097 150 : }
1098 :
1099 : /* class throw_from_call_to_external_fn_event : public throw_event. */
1100 :
1101 : void
1102 46 : throw_from_call_to_external_fn_event::print_desc (pretty_printer &pp) const
1103 : {
1104 46 : if (m_fndecl)
1105 46 : pp_printf (&pp, "if %qD throws an exception...", m_fndecl);
1106 : else
1107 0 : pp_printf (&pp, "if the called function throws an exception...");
1108 46 : }
1109 :
1110 : // class unwind_event : public checker_event
1111 :
1112 : void
1113 26 : unwind_event::print_desc (pretty_printer &pp) const
1114 : {
1115 26 : if (m_num_frames > 1)
1116 18 : pp_printf (&pp, "unwinding %i stack frames", m_num_frames);
1117 : else
1118 8 : pp_printf (&pp, "unwinding stack frame");
1119 26 : }
1120 :
1121 : diagnostics::paths::event::meaning
1122 3 : unwind_event::get_meaning () const
1123 : {
1124 3 : return meaning (verb::unwind_);
1125 : }
1126 :
1127 : /* class warning_event : public checker_event. */
1128 :
1129 : /* Implementation of diagnostics::paths::event::print_desc vfunc for
1130 : warning_event.
1131 :
1132 : If the pending diagnostic implements describe_final_event, use it,
1133 : generating a precise description e.g.
1134 : "second 'free' here; first 'free' was at (7)"
1135 :
1136 : Otherwise generate a generic description. */
1137 :
1138 : void
1139 7953 : warning_event::print_desc (pretty_printer &pp) const
1140 : {
1141 7953 : if (m_pending_diagnostic)
1142 : {
1143 7941 : tree var = fixup_tree_for_diagnostic (m_var);
1144 7941 : evdesc::final_event evd (var, m_state, *this);
1145 7941 : if (m_pending_diagnostic->describe_final_event (pp, evd))
1146 : {
1147 7322 : if (m_sm && flag_analyzer_verbose_state_changes)
1148 : {
1149 72 : if (var)
1150 64 : pp_printf (&pp, " (%qE is in state %qs)",
1151 64 : var, m_state->get_name ());
1152 : else
1153 8 : pp_printf (&pp, " (in global state %qs)",
1154 8 : m_state->get_name ());
1155 : }
1156 7322 : return;
1157 : }
1158 : }
1159 :
1160 631 : if (m_sm)
1161 : {
1162 64 : if (m_var)
1163 64 : pp_printf (&pp, "here (%qE is in state %qs)",
1164 64 : m_var, m_state->get_name ());
1165 : else
1166 0 : pp_printf (&pp, "here (in global state %qs)",
1167 0 : m_state->get_name ());
1168 64 : return;
1169 : }
1170 : else
1171 567 : pp_string (&pp, "here");
1172 : }
1173 :
1174 : /* Implementation of diagnostics::paths::event::get_meaning vfunc for
1175 : warning_event. */
1176 :
1177 : diagnostics::paths::event::meaning
1178 232 : warning_event::get_meaning () const
1179 : {
1180 232 : return meaning (verb::danger, noun::unknown);
1181 : }
1182 :
1183 : const program_state *
1184 3 : warning_event::get_program_state () const
1185 : {
1186 3 : if (m_program_state)
1187 : return m_program_state.get ();
1188 : else
1189 1 : return &m_enode->get_state ();
1190 : }
1191 :
1192 : } // namespace ana
1193 :
1194 : #endif /* #if ENABLE_ANALYZER */
|