Line data Source code
1 : /* Classes for saving, deduplicating, and emitting 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 "cfg.h"
24 : #include "gimple-pretty-print.h"
25 : #include "gimple-iterator.h"
26 : #include "inlining-iterator.h"
27 : #include "cgraph.h"
28 : #include "digraph.h"
29 : #include "gcc-rich-location.h"
30 : #include "diagnostics/sarif-sink.h"
31 :
32 : #include "analyzer/analyzer-logging.h"
33 : #include "analyzer/sm.h"
34 : #include "analyzer/pending-diagnostic.h"
35 : #include "analyzer/diagnostic-manager.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/constraint-manager.h"
41 : #include "analyzer/supergraph.h"
42 : #include "analyzer/program-state.h"
43 : #include "analyzer/exploded-graph.h"
44 : #include "analyzer/exploded-path.h"
45 : #include "analyzer/trimmed-graph.h"
46 : #include "analyzer/feasible-graph.h"
47 : #include "analyzer/checker-path.h"
48 : #include "analyzer/reachability.h"
49 :
50 : #if ENABLE_ANALYZER
51 :
52 : namespace ana {
53 :
54 : class feasible_worklist;
55 :
56 : // struct pending_location
57 :
58 925 : pending_location::pending_location ()
59 925 : : m_enode (nullptr),
60 925 : m_event_loc_info (UNKNOWN_LOCATION, NULL_TREE, 0)
61 : {
62 925 : }
63 :
64 7113 : pending_location::pending_location (exploded_node *enode)
65 7113 : : m_enode (enode),
66 7113 : m_event_loc_info (enode)
67 : {
68 7113 : }
69 :
70 3757 : pending_location::pending_location (exploded_node *enode,
71 3757 : location_t loc)
72 3757 : : m_enode (enode),
73 3757 : m_event_loc_info (enode)
74 : {
75 3757 : m_event_loc_info.m_loc = loc;
76 3757 : }
77 :
78 : std::unique_ptr<json::object>
79 36 : pending_location::to_json () const
80 : {
81 36 : auto ploc_obj = std::make_unique<json::object> ();
82 :
83 36 : if (m_enode)
84 36 : ploc_obj->set_integer ("enode", m_enode->m_index);
85 : // TODO: also store m_event_loc_info
86 36 : return ploc_obj;
87 : }
88 :
89 : /* State for finding the shortest feasible exploded_path for a
90 : saved_diagnostic.
91 : This is shared between all diagnostics, so that we avoid repeating work. */
92 :
93 : class epath_finder
94 : {
95 : public:
96 1608 : epath_finder (const exploded_graph &eg)
97 1608 : : m_eg (eg),
98 1608 : m_sep (nullptr)
99 : {
100 : /* This is shared by all diagnostics, but only needed if
101 : !flag_analyzer_feasibility. */
102 1608 : if (!flag_analyzer_feasibility)
103 4 : m_sep = new shortest_exploded_paths (eg, eg.get_origin (),
104 4 : SPS_FROM_GIVEN_ORIGIN);
105 1608 : }
106 :
107 1612 : ~epath_finder () { delete m_sep; }
108 :
109 156840 : logger *get_logger () const { return m_eg.get_logger (); }
110 :
111 : std::unique_ptr<exploded_path>
112 : get_best_epath (const exploded_node *target_enode,
113 : const pending_diagnostic &pd,
114 : const char *desc, unsigned diag_idx,
115 : std::unique_ptr<feasibility_problem> *out_problem);
116 :
117 : private:
118 : DISABLE_COPY_AND_ASSIGN(epath_finder);
119 :
120 : std::unique_ptr<exploded_path>
121 : explore_feasible_paths (const exploded_node *target_enode,
122 : const pending_diagnostic &pd,
123 : const char *desc, unsigned diag_idx);
124 : bool
125 : process_worklist_item (feasible_worklist *worklist,
126 : const trimmed_graph &tg,
127 : feasible_graph *fg,
128 : const exploded_node *target_enode,
129 : const pending_diagnostic &pd,
130 : unsigned diag_idx,
131 : std::unique_ptr<exploded_path> *out_best_path) const;
132 : void dump_trimmed_graph (const exploded_node *target_enode,
133 : const char *desc, unsigned diag_idx,
134 : const trimmed_graph &tg,
135 : const shortest_paths<eg_traits, exploded_path> &sep);
136 : void dump_feasible_graph (const exploded_node *target_enode,
137 : const char *desc, unsigned diag_idx,
138 : const feasible_graph &fg);
139 : void dump_feasible_path (const exploded_node *target_enode,
140 : unsigned diag_idx,
141 : const feasible_graph &fg,
142 : const feasible_node &fnode) const;
143 :
144 : const exploded_graph &m_eg;
145 : shortest_exploded_paths *m_sep;
146 : };
147 :
148 : /* class epath_finder. */
149 :
150 : /* Get the "best" exploded_path for reaching TARGET_ENODE from the origin,
151 : returning ownership of it to the caller.
152 :
153 : Ideally we want to report the shortest feasible path.
154 : Return nullptr if we could not find a feasible path
155 : (when flag_analyzer_feasibility is true).
156 :
157 : If flag_analyzer_feasibility is false, then simply return the
158 : shortest path.
159 :
160 : Use DESC and DIAG_IDX when logging.
161 :
162 : Write any feasibility_problem to *OUT_PROBLEM. */
163 :
164 : std::unique_ptr<exploded_path>
165 6615 : epath_finder::get_best_epath (const exploded_node *target_enode,
166 : const pending_diagnostic &pd,
167 : const char *desc, unsigned diag_idx,
168 : std::unique_ptr<feasibility_problem> *out_problem)
169 : {
170 6615 : logger *logger = get_logger ();
171 6615 : LOG_SCOPE (logger);
172 :
173 6615 : unsigned snode_id = target_enode->get_supernode ()->m_id;
174 6615 : if (logger)
175 2 : logger->log ("considering %qs at EN: %i, SN: %i (sd: %i)",
176 2 : desc, target_enode->m_index, snode_id, diag_idx);
177 :
178 : /* State-merging means that not every path in the egraph corresponds
179 : to a feasible one w.r.t. states.
180 :
181 : We want to find the shortest feasible path from the origin to ENODE
182 : in the egraph. */
183 :
184 6615 : if (flag_analyzer_feasibility)
185 : {
186 : /* Attempt to find the shortest feasible path using feasible_graph. */
187 6611 : if (logger)
188 2 : logger->log ("trying to find shortest feasible path");
189 6611 : if (std::unique_ptr<exploded_path> epath
190 6611 : = explore_feasible_paths (target_enode, pd, desc, diag_idx))
191 : {
192 6329 : if (logger)
193 2 : logger->log ("accepting %qs at EN: %i, SN: %i (sd: %i)"
194 : " with feasible path (length: %i)",
195 2 : desc, target_enode->m_index, snode_id, diag_idx,
196 : epath->length ());
197 6329 : return epath;
198 : }
199 : else
200 : {
201 282 : if (logger)
202 0 : logger->log ("rejecting %qs at EN: %i, SN: %i (sd: %i)"
203 : " due to not finding feasible path",
204 0 : desc, target_enode->m_index, snode_id, diag_idx);
205 282 : return nullptr;
206 6611 : }
207 : }
208 : else
209 : {
210 : /* As a crude approximation to shortest feasible path, simply find
211 : the shortest path, and note whether it is feasible.
212 : There could be longer feasible paths within the egraph, so this
213 : approach would lead to diagnostics being falsely rejected
214 : (PR analyzer/96374). */
215 4 : if (logger)
216 0 : logger->log ("trying to find shortest path ignoring feasibility");
217 4 : gcc_assert (m_sep);
218 4 : auto epath
219 : = std::make_unique<exploded_path>
220 4 : (m_sep->get_shortest_path (target_enode));
221 4 : if (epath->feasible_p (logger, out_problem, m_eg.get_engine (), &m_eg))
222 : {
223 0 : if (logger)
224 0 : logger->log ("accepting %qs at EN: %i, SN: %i (sn: %i)"
225 : " with feasible path (length: %i)",
226 0 : desc, target_enode->m_index, snode_id, diag_idx,
227 : epath->length ());
228 : }
229 : else
230 : {
231 4 : if (logger)
232 0 : logger->log ("accepting %qs at EN: %i, SN: %i (sn: %i) (length: %i)"
233 : " despite infeasible path (due to %qs)",
234 0 : desc, target_enode->m_index, snode_id, diag_idx,
235 : epath->length (),
236 : "-fno-analyzer-feasibility");
237 : }
238 4 : return epath;
239 4 : }
240 6615 : }
241 :
242 : /* A class for managing the worklist of feasible_nodes in
243 : epath_finder::explore_feasible_paths, prioritizing them
244 : so that shorter paths appear earlier in the queue. */
245 :
246 13222 : class feasible_worklist
247 : {
248 : public:
249 6611 : feasible_worklist (const shortest_paths<eg_traits, exploded_path> &sep)
250 13222 : : m_queue (key_t (*this, nullptr)),
251 6611 : m_sep (sep)
252 : {
253 6611 : }
254 :
255 136999 : feasible_node *take_next () { return m_queue.extract_min (); }
256 :
257 140325 : void add_node (feasible_node *fnode)
258 : {
259 280650 : m_queue.insert (key_t (*this, fnode), fnode);
260 133714 : }
261 :
262 : private:
263 : struct key_t
264 : {
265 146936 : key_t (const feasible_worklist &w, feasible_node *fnode)
266 6611 : : m_worklist (w), m_fnode (fnode)
267 : {}
268 :
269 154998 : bool operator< (const key_t &other) const
270 : {
271 154998 : return cmp (*this, other) < 0;
272 : }
273 :
274 56527 : bool operator== (const key_t &other) const
275 : {
276 56527 : return cmp (*this, other) == 0;
277 : }
278 :
279 56527 : bool operator> (const key_t &other) const
280 : {
281 56527 : return !(*this == other || *this < other);
282 : }
283 :
284 : private:
285 211525 : static int cmp (const key_t &ka, const key_t &kb)
286 : {
287 : /* Choose the node for which if the remaining path were feasible,
288 : it would be the shortest path (summing the length of the
289 : known-feasible path so far with that of the remaining
290 : possibly-feasible path). */
291 211525 : int ca = ka.m_worklist.get_estimated_cost (ka.m_fnode);
292 211525 : int cb = kb.m_worklist.get_estimated_cost (kb.m_fnode);
293 211525 : return ca - cb;
294 : }
295 :
296 : const feasible_worklist &m_worklist;
297 : feasible_node *m_fnode;
298 : };
299 :
300 : /* Get the estimated length of a path involving FNODE from
301 : the origin to the target enode.
302 : Sum the length of the known-feasible path so far with
303 : that of the remaining possibly-feasible path. */
304 :
305 423050 : int get_estimated_cost (const feasible_node *fnode) const
306 : {
307 423050 : unsigned length_so_far = fnode->get_path_length ();
308 423050 : int shortest_remaining_path
309 423050 : = m_sep.get_shortest_distance (fnode->get_inner_node ());
310 :
311 423050 : gcc_assert (shortest_remaining_path >= 0);
312 : /* This should be true since we're only exploring nodes within
313 : the trimmed graph (and we anticipate it being much smaller
314 : than this, and thus not overflowing the sum). */
315 423050 : gcc_assert (shortest_remaining_path < INT_MAX);
316 :
317 423050 : return length_so_far + shortest_remaining_path;
318 : }
319 :
320 : /* Priority queue, backed by a fibonacci_heap. */
321 : typedef fibonacci_heap<key_t, feasible_node> queue_t;
322 : queue_t m_queue;
323 : const shortest_paths<eg_traits, exploded_path> &m_sep;
324 : };
325 :
326 : /* When we're building the exploded graph we want to simplify
327 : overly-complicated symbolic values down to "UNKNOWN" to try to avoid
328 : state explosions and unbounded chains of exploration.
329 :
330 : However, when we're building the feasibility graph for a diagnostic
331 : (actually a tree), we don't want UNKNOWN values, as conditions on them
332 : are also unknown: we don't want to have a contradiction such as a path
333 : where (VAL != 0) and then (VAL == 0) along the same path.
334 :
335 : Hence this is an RAII class for temporarily disabling complexity-checking
336 : in the region_model_manager, for use within
337 : epath_finder::explore_feasible_paths.
338 :
339 : We also disable the creation of unknown_svalue instances during feasibility
340 : checking, instead creating unique svalues, to avoid paradoxes in paths. */
341 :
342 : class auto_checking_feasibility
343 : {
344 : public:
345 6611 : auto_checking_feasibility (region_model_manager *mgr) : m_mgr (mgr)
346 : {
347 6611 : m_mgr->begin_checking_feasibility ();
348 6611 : }
349 6611 : ~auto_checking_feasibility ()
350 : {
351 6611 : m_mgr->end_checking_feasibility ();
352 : }
353 : private:
354 : region_model_manager *m_mgr;
355 : };
356 :
357 : /* Attempt to find the shortest feasible path from the origin to
358 : TARGET_ENODE by iteratively building a feasible_graph, in which
359 : every path to a feasible_node is feasible by construction.
360 :
361 : We effectively explore the tree of feasible paths in order of shortest
362 : path until we either find a feasible path to TARGET_ENODE, or hit
363 : a limit and give up.
364 :
365 : Preliminaries:
366 : - Find the shortest path from each node to the TARGET_ENODE (without
367 : checking feasibility), so that we can prioritize our worklist.
368 : - Construct a trimmed_graph: the subset of nodes/edges that
369 : are on a path that eventually reaches TARGET_ENODE. We will only need
370 : to consider these when considering the shortest feasible path.
371 :
372 : Build a feasible_graph, in which every path to a feasible_node
373 : is feasible by construction.
374 : We use a worklist to flatten the exploration into an iteration.
375 : Starting at the origin, find feasible out-edges within the trimmed graph.
376 : At each stage, choose the node for which if the remaining path were feasible,
377 : it would be the shortest path (summing the length of the known-feasible path
378 : so far with that of the remaining possibly-feasible path).
379 : This way, the first feasible path we find to TARGET_ENODE is the shortest.
380 : We start by trying the shortest possible path, but if that fails,
381 : we explore progressively longer paths, eventually trying iterations through
382 : loops. The exploration is captured in the feasible_graph, which can be
383 : dumped as a .dot file to visualize the exploration. The indices of the
384 : feasible_nodes show the order in which they were created.
385 :
386 : This is something of a brute-force approach, but the trimmed_graph
387 : hopefully keeps the complexity manageable.
388 :
389 : Terminate with failure when the number of infeasible edges exceeds
390 : a threshold (--param=analyzer-max-infeasible-edges=).
391 : This is guaranteed to eventually lead to terminatation, as
392 : we can't keep creating feasible nodes without eventually
393 : either reaching an infeasible edge, or reaching the
394 : TARGET_ENODE. Specifically, there can't be a cycle of
395 : feasible edges that doesn't reach the target_enode without
396 : an out-edge that either fails feasibility or gets closer
397 : to the TARGET_ENODE: on each iteration we are either:
398 : - effectively getting closer to the TARGET_ENODE (which can't
399 : continue forever without reaching the target), or
400 : - getting monotonically closer to the termination threshold. */
401 :
402 : std::unique_ptr<exploded_path>
403 6611 : epath_finder::explore_feasible_paths (const exploded_node *target_enode,
404 : const pending_diagnostic &pd,
405 : const char *desc, unsigned diag_idx)
406 : {
407 6611 : logger *logger = get_logger ();
408 6611 : LOG_SCOPE (logger);
409 :
410 6611 : region_model_manager *mgr = m_eg.get_engine ()->get_model_manager ();
411 :
412 : /* Determine the shortest path to TARGET_ENODE from each node in
413 : the exploded graph. */
414 6611 : shortest_paths<eg_traits, exploded_path> sep
415 6611 : (m_eg, target_enode, SPS_TO_GIVEN_TARGET);
416 :
417 : /* Construct a trimmed_graph: the subset of nodes/edges that
418 : are on a path that eventually reaches TARGET_ENODE.
419 : We only need to consider these when considering the shortest
420 : feasible path. */
421 6611 : trimmed_graph tg (m_eg, target_enode);
422 :
423 6611 : if (flag_dump_analyzer_feasibility)
424 4 : dump_trimmed_graph (target_enode, desc, diag_idx, tg, sep);
425 :
426 6611 : feasible_graph fg;
427 6611 : feasible_worklist worklist (sep);
428 :
429 : /* Populate the worklist with the origin node. */
430 6611 : {
431 6611 : feasibility_state init_state (mgr, m_eg.get_supergraph ());
432 6611 : feasible_node *origin = fg.add_node (m_eg.get_origin (), init_state, 0);
433 6611 : worklist.add_node (origin);
434 6611 : }
435 :
436 : /* Iteratively explore the tree of feasible paths in order of shortest
437 : path until we either find a feasible path to TARGET_ENODE, or hit
438 : a limit. */
439 :
440 : /* Set this if we find a feasible path to TARGET_ENODE. */
441 6611 : std::unique_ptr<exploded_path> best_path = nullptr;
442 :
443 6611 : {
444 6611 : auto_checking_feasibility sentinel (mgr);
445 :
446 136999 : while (process_worklist_item (&worklist, tg, &fg, target_enode,
447 : pd, diag_idx, &best_path))
448 : {
449 : /* Empty; the work is done within process_worklist_item. */
450 : }
451 6611 : }
452 :
453 6611 : if (logger)
454 : {
455 2 : logger->log ("tg for sd: %i:", diag_idx);
456 2 : logger->inc_indent ();
457 2 : tg.log_stats (logger);
458 2 : logger->dec_indent ();
459 :
460 2 : logger->log ("fg for sd: %i:", diag_idx);
461 2 : logger->inc_indent ();
462 2 : fg.log_stats (logger);
463 2 : logger->dec_indent ();
464 : }
465 :
466 : /* Dump the feasible_graph. */
467 6611 : if (flag_dump_analyzer_feasibility)
468 4 : dump_feasible_graph (target_enode, desc, diag_idx, fg);
469 :
470 6611 : return best_path;
471 13222 : }
472 :
473 : /* Process the next item in WORKLIST, potentially adding new items
474 : based on feasible out-edges, and extending FG accordingly.
475 : Use TG to ignore out-edges that don't lead to TARGET_ENODE.
476 : Return true if the worklist processing should continue.
477 : Return false if the processing of the worklist should stop
478 : (either due to reaching TARGET_ENODE, or hitting a limit).
479 : Write to *OUT_BEST_PATH if stopping due to finding a feasible path
480 : to TARGET_ENODE.
481 : Use PD to provide additional restrictions on feasibility of
482 : the final path in the feasible_graph before converting to
483 : an exploded_path. */
484 :
485 : bool
486 136999 : epath_finder::
487 : process_worklist_item (feasible_worklist *worklist,
488 : const trimmed_graph &tg,
489 : feasible_graph *fg,
490 : const exploded_node *target_enode,
491 : const pending_diagnostic &pd,
492 : unsigned diag_idx,
493 : std::unique_ptr<exploded_path> *out_best_path) const
494 : {
495 136999 : logger *logger = get_logger ();
496 :
497 136999 : feasible_node *fnode = worklist->take_next ();
498 136999 : if (!fnode)
499 : {
500 63 : if (logger)
501 0 : logger->log ("drained worklist for sd: %i"
502 : " without finding feasible path",
503 : diag_idx);
504 63 : return false;
505 : }
506 :
507 136936 : log_scope s (logger, "fg worklist item",
508 : "considering FN: %i (EN: %i) for sd: %i",
509 136936 : fnode->get_index (), fnode->get_inner_node ()->m_index,
510 136936 : diag_idx);
511 :
512 : /* Iterate through all out-edges from this item. */
513 136936 : unsigned i;
514 136936 : exploded_edge *succ_eedge;
515 328807 : FOR_EACH_VEC_ELT (fnode->get_inner_node ()->m_succs, i, succ_eedge)
516 : {
517 198419 : log_scope s (logger, "edge", "considering edge: EN:%i -> EN:%i",
518 198419 : succ_eedge->m_src->m_index,
519 198419 : succ_eedge->m_dest->m_index);
520 : /* Reject edges that aren't in the trimmed graph. */
521 198419 : if (!tg.contains_p (succ_eedge))
522 : {
523 56054 : if (logger)
524 1 : logger->log ("rejecting: not in trimmed graph");
525 56054 : continue;
526 : }
527 :
528 142365 : feasibility_state succ_state (fnode->get_state ());
529 142365 : std::unique_ptr<rejected_constraint> rc;
530 142365 : if (succ_state.maybe_update_for_edge (logger, succ_eedge, nullptr, &rc))
531 : {
532 140105 : gcc_assert (rc == nullptr);
533 140105 : feasible_node *succ_fnode
534 140105 : = fg->add_node (succ_eedge->m_dest,
535 : succ_state,
536 140105 : fnode->get_path_length () + 1);
537 140105 : if (logger)
538 14 : logger->log ("accepting as FN: %i", succ_fnode->get_index ());
539 140105 : fg->add_edge (new feasible_edge (fnode, succ_fnode, succ_eedge));
540 :
541 : /* Have we reached TARGET_ENODE? */
542 140105 : if (succ_fnode->get_inner_node () == target_enode)
543 : {
544 6391 : if (logger)
545 2 : logger->log ("success: got feasible path to EN: %i (sd: %i)"
546 : " (length: %i)",
547 2 : target_enode->m_index, diag_idx,
548 : succ_fnode->get_path_length ());
549 6391 : if (!pd.check_valid_fpath_p (*succ_fnode))
550 : {
551 62 : if (logger)
552 0 : logger->log ("rejecting feasible path due to"
553 : " pending_diagnostic");
554 62 : return false;
555 : }
556 6329 : *out_best_path = fg->make_epath (succ_fnode);
557 6329 : if (flag_dump_analyzer_feasibility)
558 4 : dump_feasible_path (target_enode, diag_idx, *fg, *succ_fnode);
559 :
560 : /* Success: stop the worklist iteration. */
561 6329 : return false;
562 : }
563 : else
564 133714 : worklist->add_node (succ_fnode);
565 : }
566 : else
567 : {
568 2260 : if (logger)
569 0 : logger->log ("infeasible");
570 2260 : gcc_assert (rc);
571 2260 : fg->add_feasibility_problem (fnode,
572 : succ_eedge,
573 : std::move (rc));
574 :
575 : /* Give up if there have been too many infeasible edges. */
576 2260 : if (fg->get_num_infeasible ()
577 2260 : > (unsigned)param_analyzer_max_infeasible_edges)
578 : {
579 157 : if (logger)
580 0 : logger->log ("too many infeasible edges (%i); giving up",
581 : fg->get_num_infeasible ());
582 157 : return false;
583 : }
584 : }
585 198419 : }
586 :
587 : /* Continue the worklist iteration. */
588 : return true;
589 136936 : }
590 :
591 : /* Helper class for epath_finder::dump_trimmed_graph
592 : to dump extra per-node information.
593 : Use SEP to add the length of the shortest path from each
594 : node to the target node to each node's dump. */
595 :
596 : class dump_eg_with_shortest_path : public eg_traits::dump_args_t
597 : {
598 : public:
599 4 : dump_eg_with_shortest_path
600 : (const exploded_graph &eg,
601 : const shortest_paths<eg_traits, exploded_path> &sep)
602 4 : : dump_args_t (eg),
603 4 : m_sep (sep)
604 : {
605 : }
606 :
607 74 : void dump_extra_info (const exploded_node *enode,
608 : pretty_printer *pp) const final override
609 : {
610 74 : pp_printf (pp, "sp: %i", m_sep.get_shortest_path (enode).length ());
611 74 : pp_newline (pp);
612 74 : }
613 :
614 : private:
615 : const shortest_paths<eg_traits, exploded_path> &m_sep;
616 : };
617 :
618 : /* Dump TG to "BASE_NAME.DESC.DIAG_IDX.to-enN.tg.dot",
619 : annotating each node with the length of the shortest path
620 : from that node to TARGET_ENODE (using SEP). */
621 :
622 : void
623 4 : epath_finder::
624 : dump_trimmed_graph (const exploded_node *target_enode,
625 : const char *desc, unsigned diag_idx,
626 : const trimmed_graph &tg,
627 : const shortest_paths<eg_traits, exploded_path> &sep)
628 : {
629 4 : auto_timevar tv (TV_ANALYZER_DUMP);
630 4 : dump_eg_with_shortest_path inner_args (m_eg, sep);
631 4 : trimmed_graph::dump_args_t args (inner_args);
632 4 : pretty_printer pp;
633 4 : pp_printf (&pp, "%s.%s.%i.to-en%i.tg.dot",
634 4 : dump_base_name, desc, diag_idx, target_enode->m_index);
635 4 : char *filename = xstrdup (pp_formatted_text (&pp));
636 4 : tg.dump_dot (filename, nullptr, args);
637 4 : free (filename);
638 4 : }
639 :
640 : /* Dump FG to "BASE_NAME.DESC.DIAG_IDX.to-enN.fg.dot". */
641 :
642 : void
643 4 : epath_finder::dump_feasible_graph (const exploded_node *target_enode,
644 : const char *desc, unsigned diag_idx,
645 : const feasible_graph &fg)
646 : {
647 4 : auto_timevar tv (TV_ANALYZER_DUMP);
648 4 : exploded_graph::dump_args_t inner_args (m_eg);
649 4 : feasible_graph::dump_args_t args (inner_args);
650 4 : pretty_printer pp;
651 4 : pp_printf (&pp, "%s.%s.%i.to-en%i.fg.dot",
652 4 : dump_base_name, desc, diag_idx, target_enode->m_index);
653 4 : char *filename = xstrdup (pp_formatted_text (&pp));
654 4 : fg.dump_dot (filename, nullptr, args);
655 4 : free (filename);
656 4 : }
657 :
658 : /* Dump the path to FNODE to "BASE_NAME.DIAG_IDX.to-enN.fpath.txt". */
659 :
660 : void
661 4 : epath_finder::dump_feasible_path (const exploded_node *target_enode,
662 : unsigned diag_idx,
663 : const feasible_graph &fg,
664 : const feasible_node &fnode) const
665 : {
666 4 : auto_timevar tv (TV_ANALYZER_DUMP);
667 4 : pretty_printer pp;
668 4 : pp_printf (&pp, "%s.%i.to-en%i.fpath.txt",
669 4 : dump_base_name, diag_idx, target_enode->m_index);
670 4 : char *filename = xstrdup (pp_formatted_text (&pp));
671 4 : fg.dump_feasible_path (fnode, filename);
672 4 : free (filename);
673 4 : }
674 :
675 : /* class saved_diagnostic. */
676 :
677 : /* saved_diagnostic's ctor. */
678 :
679 6615 : saved_diagnostic::saved_diagnostic (const state_machine *sm,
680 : pending_location &&ploc,
681 : tree var,
682 : const svalue *sval,
683 : state_machine::state_t state,
684 : std::unique_ptr<pending_diagnostic> d,
685 6615 : unsigned idx)
686 6615 : : m_sm (sm),
687 6615 : m_ploc (std::move (ploc)),
688 6615 : m_var (var), m_sval (sval), m_state (state),
689 6615 : m_d (std::move (d)), m_trailing_eedge (nullptr),
690 6615 : m_idx (idx),
691 6615 : m_best_epath (nullptr), m_problem (nullptr),
692 6615 : m_notes ()
693 : {
694 : /* We must have an enode in order to be able to look for paths
695 : through the exploded_graph to this diagnostic. */
696 6615 : gcc_assert (m_ploc.m_enode);
697 6615 : }
698 :
699 : const supernode *
700 140098 : saved_diagnostic::get_supernode () const
701 : {
702 140098 : return m_ploc.m_enode->get_supernode ();
703 : }
704 :
705 : bool
706 47368 : saved_diagnostic::operator== (const saved_diagnostic &other) const
707 : {
708 49554 : if (m_notes.length () != other.m_notes.length ())
709 : return false;
710 46903 : for (unsigned i = 0; i < m_notes.length (); i++)
711 745 : if (!m_notes[i]->equal_p (*other.m_notes[i]))
712 : return false;
713 :
714 : // Don't deduplicate dump_path_diagnostic instances
715 46158 : if (!strcmp (m_d->get_kind (), "dump_path_diagnostic"))
716 1980 : return this == &other;
717 :
718 44178 : return (m_sm == other.m_sm
719 : /* We don't compare m_enode. */
720 37779 : && get_supernode () == other.get_supernode ()
721 7010 : && (m_ploc.m_event_loc_info.m_loc
722 7010 : == other.m_ploc.m_event_loc_info.m_loc)
723 6746 : && pending_diagnostic::same_tree_p (m_var, other.m_var)
724 6292 : && m_state == other.m_state
725 6230 : && m_d->equal_p (*other.m_d)
726 50242 : && m_trailing_eedge == other.m_trailing_eedge);
727 : }
728 :
729 : /* Add PN to this diagnostic, taking ownership of it. */
730 :
731 : void
732 199 : saved_diagnostic::add_note (std::unique_ptr<pending_note> pn)
733 : {
734 199 : gcc_assert (pn);
735 199 : m_notes.safe_push (pn.release ());
736 199 : }
737 :
738 : /* Add EVENT to this diagnostic. */
739 :
740 : void
741 164 : saved_diagnostic::add_event (std::unique_ptr<checker_event> event)
742 : {
743 164 : gcc_assert (event);
744 164 : m_saved_events.safe_push (event.release ());
745 164 : }
746 :
747 : /* Return a new json::object of the form
748 : {"sm": optional str,
749 : "ploc": {},
750 : "sval": optional str,
751 : "state": optional str,
752 : "path_length": optional int,
753 : "pending_diagnostic": str,
754 : "idx": int}. */
755 :
756 : std::unique_ptr<json::object>
757 0 : saved_diagnostic::to_json () const
758 : {
759 0 : auto sd_obj = std::make_unique<json::object> ();
760 :
761 0 : if (m_sm)
762 0 : sd_obj->set_string ("sm", m_sm->get_name ());
763 0 : sd_obj->set ("ploc", m_ploc.to_json ());
764 0 : if (m_sval)
765 0 : sd_obj->set ("sval", m_sval->to_json ());
766 0 : if (m_state)
767 0 : sd_obj->set ("state", m_state->to_json ());
768 0 : if (m_best_epath)
769 0 : sd_obj->set_integer ("path_length", get_epath_length ());
770 0 : sd_obj->set_string ("pending_diagnostic", m_d->get_kind ());
771 0 : sd_obj->set_integer ("idx", m_idx);
772 :
773 0 : return sd_obj;
774 : }
775 :
776 : /* Dump this to PP in a form suitable for use as an id in .dot output. */
777 :
778 : void
779 16 : saved_diagnostic::dump_dot_id (pretty_printer *pp) const
780 : {
781 16 : pp_printf (pp, "sd_%i", m_idx);
782 16 : }
783 :
784 : /* Dump this to PP in a form suitable for use as a node in .dot output. */
785 :
786 : void
787 8 : saved_diagnostic::dump_as_dot_node (pretty_printer *pp) const
788 : {
789 8 : dump_dot_id (pp);
790 8 : pp_printf (pp,
791 : " [shape=none,margin=0,style=filled,fillcolor=\"red\",label=\"");
792 8 : pp_write_text_to_stream (pp);
793 :
794 : /* Node label. */
795 8 : pp_printf (pp, "DIAGNOSTIC: %s (sd: %i)\n",
796 8 : m_d->get_kind (), m_idx);
797 8 : if (m_sm)
798 : {
799 8 : pp_printf (pp, "sm: %s", m_sm->get_name ());
800 8 : if (m_state)
801 : {
802 8 : pp_printf (pp, "; state: ");
803 8 : m_state->dump_to_pp (pp);
804 : }
805 8 : pp_newline (pp);
806 : }
807 8 : if (m_var)
808 8 : pp_printf (pp, "var: %qE\n", m_var);
809 8 : if (m_sval)
810 : {
811 8 : pp_string (pp, "sval: ");
812 8 : m_sval->dump_to_pp (pp, true);
813 8 : pp_newline (pp);
814 : }
815 8 : if (m_best_epath)
816 0 : pp_printf (pp, "path length: %i\n", get_epath_length ());
817 :
818 8 : pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
819 8 : pp_string (pp, "\"];\n\n");
820 :
821 : /* Show links to duplicates. */
822 8 : for (auto iter : m_duplicates)
823 : {
824 0 : dump_dot_id (pp);
825 0 : pp_string (pp, " -> ");
826 0 : iter->dump_dot_id (pp);
827 0 : pp_string (pp, " [style=\"dotted\" arrowhead=\"none\"];");
828 0 : pp_newline (pp);
829 : }
830 8 : }
831 :
832 : /* Use PF to find the best exploded_path for this saved_diagnostic, if any,
833 : and store it in m_best_epath.
834 : Return true if a best path was found. */
835 :
836 : bool
837 6615 : saved_diagnostic::calc_best_epath (epath_finder *pf)
838 : {
839 6615 : logger *logger = pf->get_logger ();
840 6615 : LOG_SCOPE (logger);
841 6615 : m_problem = nullptr;
842 :
843 6615 : m_best_epath = pf->get_best_epath (m_ploc.m_enode,
844 6615 : *m_d, m_d->get_kind (), m_idx,
845 6615 : &m_problem);
846 :
847 : /* Handle failure to find a feasible path. */
848 6615 : if (m_best_epath == nullptr)
849 282 : return false;
850 :
851 : gcc_assert (m_best_epath);
852 :
853 : return true;
854 6615 : }
855 :
856 : unsigned
857 6756 : saved_diagnostic::get_epath_length () const
858 : {
859 6756 : gcc_assert (m_best_epath);
860 6756 : return m_best_epath->length ();
861 : }
862 :
863 : /* Record that OTHER (and its duplicates) are duplicates
864 : of this saved_diagnostic. */
865 :
866 : void
867 2131 : saved_diagnostic::add_duplicate (saved_diagnostic *other)
868 : {
869 2131 : gcc_assert (other);
870 2131 : m_duplicates.reserve (m_duplicates.length ()
871 2235 : + other->m_duplicates.length ()
872 : + 1);
873 2131 : m_duplicates.splice (other->m_duplicates);
874 2131 : other->m_duplicates.truncate (0);
875 2131 : m_duplicates.safe_push (other);
876 2131 : }
877 :
878 : /* Walk up the sedges of each of the two paths.
879 : If the two sequences of sedges do not perfectly correspond,
880 : then paths are incompatible.
881 : If there is at least one sedge that either cannot be paired up
882 : or its counterpart is not equal, then the paths are incompatible
883 : and this function returns FALSE.
884 : Otherwise return TRUE.
885 :
886 : Incompatible paths:
887 :
888 : <cond Y>
889 : / \
890 : / \
891 : true false
892 : | |
893 : ... ...
894 : | |
895 : ... stmt x
896 : |
897 : stmt x
898 :
899 : Both LHS_PATH and RHS_PATH final enodes should be
900 : over the same gimple statement. */
901 :
902 : static bool
903 73 : compatible_epath_p (const exploded_path *lhs_path,
904 : const exploded_path *rhs_path)
905 : {
906 73 : gcc_assert (lhs_path);
907 73 : gcc_assert (rhs_path);
908 73 : gcc_assert (rhs_path->length () > 0);
909 73 : gcc_assert (rhs_path->length () > 0);
910 73 : int lhs_eedge_idx = lhs_path->length () - 1;
911 73 : int rhs_eedge_idx = rhs_path->length () - 1;
912 73 : const exploded_edge *lhs_eedge;
913 73 : const exploded_edge *rhs_eedge;
914 :
915 1368 : while (lhs_eedge_idx >= 0 && rhs_eedge_idx >= 0)
916 : {
917 1461 : while (lhs_eedge_idx >= 0)
918 : {
919 : /* Find LHS_PATH's next superedge. */
920 1420 : lhs_eedge = lhs_path->m_elements[lhs_eedge_idx].m_eedge;
921 1420 : if (lhs_eedge->m_sedge)
922 : break;
923 : else
924 93 : lhs_eedge_idx--;
925 : }
926 1461 : while (rhs_eedge_idx >= 0)
927 : {
928 : /* Find RHS_PATH's next superedge. */
929 1420 : rhs_eedge = rhs_path->m_elements[rhs_eedge_idx].m_eedge;
930 1420 : if (rhs_eedge->m_sedge)
931 : break;
932 : else
933 93 : rhs_eedge_idx--;
934 : }
935 :
936 1368 : if (lhs_eedge->m_sedge && rhs_eedge->m_sedge)
937 : {
938 1327 : if (lhs_eedge->m_sedge != rhs_eedge->m_sedge)
939 : /* Both superedges do not match.
940 : Superedges are not dependent on the exploded path, so even
941 : different epaths will have similar sedges if they follow
942 : the same outcome of a conditional node. */
943 : return false;
944 :
945 1295 : lhs_eedge_idx--;
946 1295 : rhs_eedge_idx--;
947 1295 : continue;
948 : }
949 41 : else if (lhs_eedge->m_sedge == nullptr && rhs_eedge->m_sedge == nullptr)
950 : /* Both paths were drained up entirely.
951 : No discriminant was found. */
952 : return true;
953 :
954 : /* A superedge was found for only one of the two paths. */
955 : return false;
956 : }
957 :
958 : /* A superedge was found for only one of the two paths. */
959 0 : if (lhs_eedge_idx >= 0 || rhs_eedge_idx >= 0)
960 : return false;
961 :
962 : /* Both paths were drained up entirely.
963 : No discriminant was found. */
964 : return true;
965 : }
966 :
967 :
968 : /* Return true if this diagnostic supercedes OTHER, and that OTHER should
969 : therefore not be emitted. */
970 :
971 : bool
972 28107 : saved_diagnostic::supercedes_p (const saved_diagnostic &other) const
973 : {
974 28107 : if (get_supernode () != other.get_supernode ())
975 : return false;
976 :
977 : /* return early if OTHER won't be superseded anyway. */
978 5086 : if (!m_d->supercedes_p (*other.m_d))
979 : return false;
980 :
981 : /* If the two saved_diagnostics' path are not compatible
982 : then they cannot supersede one another. */
983 73 : return compatible_epath_p (m_best_epath.get (), other.m_best_epath.get ());
984 : }
985 :
986 : /* Move any saved checker_events from this saved_diagnostic to
987 : the end of DST_PATH. */
988 :
989 : void
990 4161 : saved_diagnostic::add_any_saved_events (checker_path &dst_path)
991 : {
992 4617 : for (auto &event : m_saved_events)
993 : {
994 152 : dst_path.add_event (std::unique_ptr<checker_event> (event));
995 152 : event = nullptr;
996 : }
997 4161 : }
998 :
999 : /* Emit any pending notes owned by this diagnostic. */
1000 :
1001 : void
1002 4092 : saved_diagnostic::emit_any_notes () const
1003 : {
1004 4625 : for (auto pn : m_notes)
1005 183 : pn->emit ();
1006 4092 : }
1007 :
1008 : /* For SARIF output, add additional properties to the "result" object
1009 : for this diagnostic.
1010 : This extra data is intended for use when debugging the analyzer. */
1011 :
1012 : void
1013 36 : saved_diagnostic::
1014 : maybe_add_sarif_properties (diagnostics::sarif_object &result_obj) const
1015 : {
1016 36 : auto &props = result_obj.get_or_create_properties ();
1017 : #define PROPERTY_PREFIX "gcc/analyzer/saved_diagnostic/"
1018 36 : if (m_sm)
1019 16 : props.set_string (PROPERTY_PREFIX "sm", m_sm->get_name ());
1020 36 : props.set (PROPERTY_PREFIX "ploc", m_ploc.to_json ());
1021 36 : if (m_var)
1022 16 : props.set (PROPERTY_PREFIX "var", tree_to_json (m_var));
1023 36 : if (m_sval)
1024 16 : props.set (PROPERTY_PREFIX "sval", m_sval->to_json ());
1025 36 : if (m_state)
1026 16 : props.set (PROPERTY_PREFIX "state", m_state->to_json ());
1027 : // TODO: m_best_epath
1028 36 : props.set_integer (PROPERTY_PREFIX "idx", m_idx);
1029 36 : if (m_duplicates.length () > 0)
1030 : {
1031 4 : auto duplicates_arr = std::make_unique<json::array> ();
1032 16 : for (auto iter : m_duplicates)
1033 : {
1034 4 : auto sd_obj = std::make_unique<diagnostics::sarif_object> ();
1035 4 : iter->maybe_add_sarif_properties (*sd_obj);
1036 4 : duplicates_arr->append (std::move (sd_obj));
1037 4 : }
1038 4 : props.set<json::array> (PROPERTY_PREFIX "duplicates",
1039 : std::move (duplicates_arr));
1040 4 : }
1041 : #undef PROPERTY_PREFIX
1042 :
1043 : #define PROPERTY_PREFIX "gcc/analyzer/pending_diagnostic/"
1044 36 : props.set_string (PROPERTY_PREFIX "kind", m_d->get_kind ());
1045 : #undef PROPERTY_PREFIX
1046 :
1047 : /* Potentially add pending_diagnostic-specific properties. */
1048 36 : m_d->maybe_add_sarif_properties (result_obj);
1049 36 : }
1050 :
1051 : /* State for building a checker_path from a particular exploded_path.
1052 : In particular, this precomputes reachability information: the set of
1053 : source enodes for which a path be found to the diagnostic enode. */
1054 :
1055 4161 : class path_builder
1056 : {
1057 : public:
1058 4161 : path_builder (const exploded_graph &eg,
1059 : const exploded_path &epath,
1060 : const feasibility_problem *problem,
1061 : const saved_diagnostic &sd)
1062 4161 : : m_eg (eg),
1063 4161 : m_diag_enode (epath.get_final_enode ()),
1064 4161 : m_sd (sd),
1065 4161 : m_reachability (eg, m_diag_enode),
1066 4161 : m_feasibility_problem (problem)
1067 4161 : {}
1068 :
1069 0 : const exploded_node *get_diag_node () const { return m_diag_enode; }
1070 :
1071 58165 : pending_diagnostic *get_pending_diagnostic () const
1072 : {
1073 207 : return m_sd.m_d.get ();
1074 : }
1075 :
1076 32820 : bool reachable_from_p (const exploded_node *src_enode) const
1077 : {
1078 65640 : return m_reachability.reachable_from_p (src_enode);
1079 : }
1080 :
1081 86474 : const extrinsic_state &get_ext_state () const { return m_eg.get_ext_state (); }
1082 :
1083 41464 : const feasibility_problem *get_feasibility_problem () const
1084 : {
1085 41464 : return m_feasibility_problem;
1086 : }
1087 :
1088 4503 : const state_machine *get_sm () const { return m_sd.m_sm; }
1089 :
1090 : const supergraph &
1091 43235 : get_supergraph () const { return m_eg.get_supergraph (); }
1092 :
1093 : private:
1094 : typedef reachability<eg_traits> enode_reachability;
1095 :
1096 : const exploded_graph &m_eg;
1097 :
1098 : /* The enode where the diagnostic occurs. */
1099 : const exploded_node *m_diag_enode;
1100 :
1101 : const saved_diagnostic &m_sd;
1102 :
1103 : /* Precompute all enodes from which the diagnostic is reachable. */
1104 : enode_reachability m_reachability;
1105 :
1106 : const feasibility_problem *m_feasibility_problem;
1107 : };
1108 :
1109 : /* class diagnostic_manager. */
1110 :
1111 : /* diagnostic_manager's ctor. */
1112 :
1113 3423 : diagnostic_manager::diagnostic_manager (logger *logger, engine *eng,
1114 3423 : int verbosity)
1115 3423 : : log_user (logger), m_eng (eng), m_verbosity (verbosity),
1116 3423 : m_num_disabled_diagnostics (0)
1117 : {
1118 3423 : }
1119 :
1120 : /* Queue pending_diagnostic D at ENODE for later emission.
1121 : Return true/false signifying if the diagnostic was actually added. */
1122 :
1123 : bool
1124 10722 : diagnostic_manager::add_diagnostic (const state_machine *sm,
1125 : pending_location &&ploc,
1126 : tree var,
1127 : const svalue *sval,
1128 : state_machine::state_t state,
1129 : std::unique_ptr<pending_diagnostic> d)
1130 : {
1131 10722 : LOG_FUNC (get_logger ());
1132 :
1133 : /* We must have an enode in order to be able to look for paths
1134 : through the exploded_graph to the diagnostic. */
1135 10722 : gcc_assert (ploc.m_enode);
1136 :
1137 : /* If this warning is ultimately going to be rejected by a -Wno-analyzer-*
1138 : flag, reject it now. */
1139 10722 : {
1140 10722 : location_t loc = ploc.m_event_loc_info.m_loc;
1141 10722 : loc = d->fixup_location (loc, true);
1142 10722 : int option = d->get_controlling_option ();
1143 10722 : if (!warning_enabled_at (loc, option))
1144 : {
1145 4107 : if (get_logger ())
1146 0 : get_logger ()->log ("rejecting disabled warning %qs",
1147 0 : d->get_kind ());
1148 4107 : m_num_disabled_diagnostics++;
1149 4107 : return false;
1150 : }
1151 : }
1152 :
1153 6615 : saved_diagnostic *sd
1154 : = new saved_diagnostic (sm,
1155 : std::move (ploc),
1156 : var,
1157 : sval,
1158 : state,
1159 : std::move (d),
1160 11622 : m_saved_diagnostics.length ());
1161 6615 : m_saved_diagnostics.safe_push (sd);
1162 6615 : sd->m_ploc.m_enode->add_diagnostic (sd);
1163 6615 : if (get_logger ())
1164 4 : log ("adding saved diagnostic %i at SN %i to EN %i: %qs",
1165 : sd->get_index (),
1166 2 : sd->get_supernode ()->m_id,
1167 2 : sd->m_ploc.m_enode->m_index,
1168 2 : sd->m_d->get_kind ());
1169 : return true;
1170 10722 : }
1171 :
1172 : /* Queue pending_diagnostic D at ENODE for later emission.
1173 : Return true/false signifying if the diagnostic was actually added.
1174 : Take ownership of D (or delete it). */
1175 :
1176 : bool
1177 3773 : diagnostic_manager::add_diagnostic (pending_location &&ploc,
1178 : std::unique_ptr<pending_diagnostic> d)
1179 : {
1180 3773 : gcc_assert (ploc.m_enode);
1181 3773 : return add_diagnostic (nullptr, std::move (ploc),
1182 3773 : NULL_TREE, nullptr, 0, std::move (d));
1183 : }
1184 :
1185 : /* Add PN to the most recent saved_diagnostic. */
1186 :
1187 : void
1188 199 : diagnostic_manager::add_note (std::unique_ptr<pending_note> pn)
1189 : {
1190 199 : LOG_FUNC (get_logger ());
1191 199 : gcc_assert (pn);
1192 :
1193 : /* Get most recent saved_diagnostic. */
1194 199 : gcc_assert (m_saved_diagnostics.length () > 0);
1195 199 : saved_diagnostic *sd = m_saved_diagnostics[m_saved_diagnostics.length () - 1];
1196 199 : sd->add_note (std::move (pn));
1197 199 : }
1198 :
1199 : /* Add EVENT to the most recent saved_diagnostic. */
1200 :
1201 : void
1202 164 : diagnostic_manager::add_event (std::unique_ptr<checker_event> event)
1203 : {
1204 164 : LOG_FUNC (get_logger ());
1205 164 : gcc_assert (event);
1206 :
1207 : /* Get most recent saved_diagnostic. */
1208 164 : gcc_assert (m_saved_diagnostics.length () > 0);
1209 164 : saved_diagnostic *sd = m_saved_diagnostics[m_saved_diagnostics.length () - 1];
1210 164 : sd->add_event (std::move (event));
1211 164 : }
1212 :
1213 : /* Return a new json::object of the form
1214 : {"diagnostics" : [obj for saved_diagnostic]}. */
1215 :
1216 : std::unique_ptr<json::object>
1217 0 : diagnostic_manager::to_json () const
1218 : {
1219 0 : auto dm_obj = std::make_unique<json::object> ();
1220 :
1221 0 : {
1222 0 : auto sd_arr = std::make_unique<json::array> ();
1223 0 : int i;
1224 0 : saved_diagnostic *sd;
1225 0 : FOR_EACH_VEC_ELT (m_saved_diagnostics, i, sd)
1226 0 : sd_arr->append (sd->to_json ());
1227 0 : dm_obj->set ("diagnostics", std::move (sd_arr));
1228 0 : }
1229 :
1230 0 : return dm_obj;
1231 : }
1232 :
1233 : /* A class for identifying sets of duplicated pending_diagnostic.
1234 :
1235 : We want to find the simplest saved_diagnostic amongst those that share a
1236 : dedupe_key. */
1237 :
1238 : class dedupe_key
1239 : {
1240 : public:
1241 6333 : dedupe_key (const saved_diagnostic &sd)
1242 6333 : : m_sd (sd),
1243 6333 : m_loc (sd.m_ploc.m_event_loc_info.m_loc)
1244 : {
1245 : }
1246 :
1247 63761 : hashval_t hash () const
1248 : {
1249 63761 : inchash::hash hstate;
1250 : // TODO: m_sd
1251 63761 : return hstate.end ();
1252 : }
1253 47368 : bool operator== (const dedupe_key &other) const
1254 : {
1255 47368 : return (m_sd == other.m_sd
1256 47368 : && m_loc == other.m_loc);
1257 : }
1258 :
1259 30312 : location_t get_location () const { return m_loc; }
1260 :
1261 : /* A qsort comparator for use by dedupe_winners::emit_best
1262 : to sort them into location_t order. */
1263 :
1264 : static int
1265 30312 : comparator (const void *p1, const void *p2)
1266 : {
1267 30312 : const dedupe_key *pk1 = *(const dedupe_key * const *)p1;
1268 30312 : const dedupe_key *pk2 = *(const dedupe_key * const *)p2;
1269 :
1270 30312 : location_t loc1 = pk1->get_location ();
1271 30312 : location_t loc2 = pk2->get_location ();
1272 :
1273 30312 : if (int cmp = linemap_compare_locations (line_table, loc2, loc1))
1274 : return cmp;
1275 1245 : if (int cmp = ((int)pk1->m_sd.get_epath_length ()
1276 1245 : - (int)pk2->m_sd.get_epath_length ()))
1277 : return cmp;
1278 602 : if (int cmp = strcmp (pk1->m_sd.m_d->get_kind (),
1279 602 : pk2->m_sd.m_d->get_kind ()))
1280 : return cmp;
1281 : return 0;
1282 : }
1283 :
1284 : const saved_diagnostic &m_sd;
1285 : location_t m_loc;
1286 : };
1287 :
1288 : /* Traits for use by dedupe_winners. */
1289 :
1290 : class dedupe_hash_map_traits
1291 : {
1292 : public:
1293 : typedef const dedupe_key *key_type;
1294 : typedef saved_diagnostic *value_type;
1295 : typedef saved_diagnostic *compare_type;
1296 :
1297 63761 : static inline hashval_t hash (const key_type &v)
1298 : {
1299 63761 : return v->hash ();
1300 : }
1301 47368 : static inline bool equal_keys (const key_type &k1, const key_type &k2)
1302 : {
1303 47368 : return *k1 == *k2;
1304 : }
1305 : template <typename T>
1306 : static inline void remove (T &)
1307 : {
1308 : // TODO
1309 : }
1310 : template <typename T>
1311 41 : static inline void mark_deleted (T &entry)
1312 : {
1313 41 : entry.m_key = reinterpret_cast<key_type> (1);
1314 : }
1315 : template <typename T>
1316 0 : static inline void mark_empty (T &entry)
1317 : {
1318 0 : entry.m_key = nullptr;
1319 : }
1320 : template <typename T>
1321 144388 : static inline bool is_deleted (const T &entry)
1322 : {
1323 144388 : return entry.m_key == reinterpret_cast<key_type> (1);
1324 : }
1325 : template <typename T>
1326 532677 : static inline bool is_empty (const T &entry)
1327 : {
1328 532677 : return entry.m_key == nullptr;
1329 : }
1330 : static const bool empty_zero_p = true;
1331 : };
1332 :
1333 : /* A class for deduplicating diagnostics and finding (and emitting) the
1334 : best saved_diagnostic within each partition. */
1335 :
1336 : class dedupe_winners
1337 : {
1338 : public:
1339 1608 : ~dedupe_winners ()
1340 : {
1341 : /* Delete all keys, but not the saved_diagnostics. */
1342 5769 : for (map_t::iterator iter = m_map.begin ();
1343 5769 : iter != m_map.end ();
1344 4161 : ++iter)
1345 4161 : delete (*iter).first;
1346 1608 : }
1347 :
1348 : /* Determine an exploded_path for SD using PF and, if it's feasible,
1349 : determine if SD is the best seen so far for its dedupe_key.
1350 : Record the winning SD for each dedupe_key. */
1351 :
1352 6615 : void add (logger *logger,
1353 : epath_finder *pf,
1354 : saved_diagnostic *sd)
1355 : {
1356 : /* Determine best epath for SD. */
1357 6615 : if (!sd->calc_best_epath (pf))
1358 282 : return;
1359 :
1360 6333 : const exploded_path *epath = sd->get_best_epath ();
1361 6333 : gcc_assert (epath);
1362 :
1363 : /* Now we have an exploded path, use it for pending_locations that are
1364 : affected by such things, and for deduplication. */
1365 6333 : if (sd->m_ploc.m_fixer_for_epath)
1366 1196 : sd->m_ploc.m_fixer_for_epath->fixup_for_epath (*epath, sd->m_ploc);
1367 :
1368 6333 : dedupe_key *key = new dedupe_key (*sd);
1369 6333 : if (saved_diagnostic **slot = m_map.get (key))
1370 : {
1371 2131 : if (logger)
1372 0 : logger->log ("already have this dedupe_key");
1373 :
1374 2131 : saved_diagnostic *cur_best_sd = *slot;
1375 :
1376 2131 : if (sd->get_epath_length () < cur_best_sd->get_epath_length ())
1377 : {
1378 : /* We've got a shorter path for the key; replace
1379 : the current candidate, marking it as a duplicate of SD. */
1380 145 : if (logger)
1381 0 : logger->log ("length %i is better than existing length %i;"
1382 : " taking over this dedupe_key",
1383 : sd->get_epath_length (),
1384 : cur_best_sd->get_epath_length ());
1385 145 : sd->add_duplicate (cur_best_sd);
1386 145 : *slot = sd;
1387 : }
1388 : else
1389 : /* We haven't beaten the current best candidate; add SD
1390 : as a duplicate of it. */
1391 : {
1392 1986 : if (logger)
1393 0 : logger->log ("length %i isn't better than existing length %i;"
1394 : " dropping this candidate",
1395 : sd->get_epath_length (),
1396 : cur_best_sd->get_epath_length ());
1397 1986 : cur_best_sd->add_duplicate (sd);
1398 : }
1399 2131 : delete key;
1400 : }
1401 : else
1402 : {
1403 : /* This is the first candidate for this key. */
1404 4202 : m_map.put (key, sd);
1405 4202 : if (logger)
1406 2 : logger->log ("first candidate for this dedupe_key");
1407 : }
1408 : }
1409 :
1410 : /* Handle interactions between the dedupe winners, so that some
1411 : diagnostics can supercede others (of different kinds).
1412 :
1413 : We want use-after-free to supercede use-of-unitialized-value,
1414 : so that if we have these at the same stmt, we don't emit
1415 : a use-of-uninitialized, just the use-after-free. */
1416 :
1417 1608 : void handle_interactions (diagnostic_manager *dm)
1418 : {
1419 1608 : LOG_SCOPE (dm->get_logger ());
1420 1608 : auto_vec<const dedupe_key *> superceded;
1421 5810 : for (auto outer : m_map)
1422 : {
1423 4202 : const saved_diagnostic *outer_sd = outer.second;
1424 64536 : for (auto inner : m_map)
1425 : {
1426 28107 : const saved_diagnostic *inner_sd = inner.second;
1427 28107 : if (inner_sd->supercedes_p (*outer_sd))
1428 : {
1429 41 : superceded.safe_push (outer.first);
1430 41 : if (dm->get_logger ())
1431 0 : dm->log ("sd[%i] \"%s\" superceded by sd[%i] \"%s\"",
1432 0 : outer_sd->get_index (), outer_sd->m_d->get_kind (),
1433 0 : inner_sd->get_index (), inner_sd->m_d->get_kind ());
1434 : break;
1435 : }
1436 : }
1437 : }
1438 1731 : for (auto iter : superceded)
1439 41 : m_map.remove (iter);
1440 1608 : }
1441 :
1442 : /* Emit the simplest diagnostic within each set. */
1443 :
1444 1608 : void emit_best (diagnostic_manager *dm,
1445 : const exploded_graph &eg)
1446 : {
1447 1608 : LOG_SCOPE (dm->get_logger ());
1448 :
1449 : /* Get keys into a vec for sorting. */
1450 1608 : auto_vec<const dedupe_key *> keys (m_map.elements ());
1451 1608 : for (map_t::iterator iter = m_map.begin ();
1452 5769 : iter != m_map.end ();
1453 4161 : ++iter)
1454 4161 : keys.quick_push ((*iter).first);
1455 :
1456 3167 : dm->log ("# keys after de-duplication: %i", keys.length ());
1457 :
1458 : /* Sort into a good emission order. */
1459 1608 : keys.qsort (dedupe_key::comparator);
1460 :
1461 : /* Emit the best saved_diagnostics for each key. */
1462 : int i;
1463 : const dedupe_key *key;
1464 7328 : FOR_EACH_VEC_ELT (keys, i, key)
1465 : {
1466 4161 : saved_diagnostic **slot = m_map.get (key);
1467 4161 : gcc_assert (*slot);
1468 4161 : saved_diagnostic *sd = *slot;
1469 4161 : dm->emit_saved_diagnostic (eg, *sd);
1470 : }
1471 1608 : }
1472 :
1473 : private:
1474 : /* This maps from each dedupe_key to a current best saved_diagnostic. */
1475 :
1476 : typedef hash_map<const dedupe_key *, saved_diagnostic *,
1477 : dedupe_hash_map_traits> map_t;
1478 : map_t m_map;
1479 : };
1480 :
1481 : /* Emit all saved diagnostics. */
1482 :
1483 : void
1484 3423 : diagnostic_manager::emit_saved_diagnostics (const exploded_graph &eg)
1485 : {
1486 3423 : LOG_SCOPE (get_logger ());
1487 3423 : auto_timevar tv (TV_ANALYZER_DIAGNOSTICS);
1488 5031 : log ("# saved diagnostics: %i", m_saved_diagnostics.length ());
1489 3423 : log ("# disabled diagnostics: %i", m_num_disabled_diagnostics);
1490 3423 : if (get_logger ())
1491 : {
1492 : unsigned i;
1493 : saved_diagnostic *sd;
1494 7 : FOR_EACH_VEC_ELT (m_saved_diagnostics, i, sd)
1495 2 : log ("[%i] sd: %qs at EN: %i, SN: %i",
1496 2 : i, sd->m_d->get_kind (), sd->m_ploc.m_enode->m_index,
1497 2 : sd->get_supernode ()->m_id);
1498 : }
1499 :
1500 3423 : if (m_saved_diagnostics.length () == 0)
1501 1815 : return;
1502 :
1503 : /* Compute the shortest_paths once, sharing it between all diagnostics. */
1504 1608 : epath_finder pf (eg);
1505 :
1506 : /* Iterate through all saved diagnostics, adding them to a dedupe_winners
1507 : instance. This partitions the saved diagnostics by dedupe_key,
1508 : generating exploded_paths for them, and retaining the best one in each
1509 : partition. */
1510 1608 : dedupe_winners best_candidates;
1511 :
1512 1608 : int i;
1513 1608 : saved_diagnostic *sd;
1514 9831 : FOR_EACH_VEC_ELT (m_saved_diagnostics, i, sd)
1515 6615 : best_candidates.add (get_logger (), &pf, sd);
1516 :
1517 1608 : best_candidates.handle_interactions (this);
1518 :
1519 : /* For each dedupe-key, call emit_saved_diagnostic on the "best"
1520 : saved_diagnostic. */
1521 1608 : best_candidates.emit_best (this, eg);
1522 3423 : }
1523 :
1524 : /* Custom subclass of diagnostics::metadata which, for SARIF output,
1525 : populates the property bag of the diagnostic's "result" object
1526 : with information from the saved_diagnostic and the
1527 : pending_diagnostic. */
1528 :
1529 4161 : class pending_diagnostic_metadata : public diagnostics::metadata
1530 : {
1531 : public:
1532 4161 : pending_diagnostic_metadata (const saved_diagnostic &sd)
1533 4161 : : m_sd (sd)
1534 : {
1535 : }
1536 :
1537 : void
1538 32 : maybe_add_sarif_properties (diagnostics::sarif_object &result_obj)
1539 : const override
1540 : {
1541 32 : m_sd.maybe_add_sarif_properties (result_obj);
1542 32 : }
1543 :
1544 : private:
1545 : const saved_diagnostic &m_sd;
1546 : };
1547 :
1548 : /* Given a saved_diagnostic SD with m_best_epath through EG,
1549 : create an checker_path of suitable events and use it to call
1550 : SD's underlying pending_diagnostic "emit" vfunc to emit a diagnostic. */
1551 :
1552 : void
1553 4161 : diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg,
1554 : saved_diagnostic &sd)
1555 : {
1556 4161 : LOG_SCOPE (get_logger ());
1557 4161 : log ("sd[%i]: %qs at SN: %i",
1558 4161 : sd.get_index (), sd.m_d->get_kind (), sd.get_supernode ()->m_id);
1559 5595 : log ("num dupes: %i", sd.get_num_dupes ());
1560 :
1561 4161 : exploded_path *epath = sd.get_best_epath ();
1562 4161 : gcc_assert (epath);
1563 :
1564 4161 : epath->maybe_log (get_logger (), "best epath");
1565 :
1566 : /* Precompute all enodes from which the diagnostic is reachable. */
1567 4161 : path_builder pb (eg, *epath, sd.get_feasibility_problem (), sd);
1568 :
1569 : /* Annotate EPATH with information specific to the diagnostic, such
1570 : as pertinent data flow events. */
1571 4161 : annotate_exploded_path (pb, *epath);
1572 4161 : epath->maybe_log (get_logger (), "best epath with annotations");
1573 :
1574 : /* This is the diagnostics::paths::path instance that will be built for
1575 : the diagnostic. */
1576 4161 : checker_path emission_path (get_logical_location_manager (),
1577 : eg.get_ext_state (),
1578 4161 : get_logger ());
1579 :
1580 : /* Populate emission_path with a full description of EPATH. */
1581 4161 : build_emission_path (pb, *epath, &emission_path);
1582 :
1583 : /* Now prune it to just cover the most pertinent events. */
1584 4161 : prune_path (&emission_path, sd.m_sm, sd.m_sval, sd.m_state);
1585 :
1586 : /* Add any saved events to the path, giving contextual information
1587 : about what the analyzer was simulating as the diagnostic was
1588 : generated. These don't get pruned, as they are probably pertinent. */
1589 4161 : sd.add_any_saved_events (emission_path);
1590 :
1591 : /* Add a final event to the path, covering the diagnostic itself. */
1592 4161 : {
1593 4161 : const exploded_node *const enode = epath->get_final_enode ();
1594 4161 : sd.m_d->add_final_event (sd.m_sm, enode, sd.m_ploc.m_event_loc_info,
1595 : sd.m_var, sd.m_state, &emission_path);
1596 : }
1597 :
1598 : /* The "final" event might not be final; if the saved_diagnostic has a
1599 : trailing eedge stashed, add any events for it. This is for use
1600 : in handling longjmp, to show where a longjmp is rewinding to. */
1601 4161 : if (sd.m_trailing_eedge)
1602 4 : add_events_for_eedge (pb, *sd.m_trailing_eedge, &emission_path, nullptr,
1603 : nullptr);
1604 :
1605 4161 : emission_path.inject_any_inlined_call_events (get_logger ());
1606 :
1607 4161 : emission_path.prepare_for_emission (sd.m_d.get ());
1608 :
1609 4161 : location_t loc = sd.m_ploc.m_event_loc_info.m_loc;
1610 4161 : loc = sd.m_d->fixup_location (loc, true);
1611 :
1612 : /* Allow the pending_diagnostic to fix up the locations of events. */
1613 4161 : emission_path.fixup_locations (sd.m_d.get ());
1614 :
1615 4161 : gcc_rich_location rich_loc (loc);
1616 4161 : rich_loc.set_path (&emission_path);
1617 :
1618 4161 : auto_diagnostic_group d;
1619 4161 : auto_cfun sentinel (sd.get_supernode ()->m_fun);
1620 4161 : pending_diagnostic_metadata m (sd);
1621 4161 : diagnostic_emission_context diag_ctxt (sd, rich_loc, m, get_logger ());
1622 4161 : if (sd.m_d->emit (diag_ctxt))
1623 : {
1624 4092 : sd.emit_any_notes ();
1625 :
1626 4092 : unsigned num_dupes = sd.get_num_dupes ();
1627 4092 : if (flag_analyzer_show_duplicate_count && num_dupes > 0)
1628 4 : inform_n (loc, num_dupes,
1629 : "%i duplicate", "%i duplicates",
1630 : num_dupes);
1631 4092 : if (flag_dump_analyzer_exploded_paths)
1632 : {
1633 0 : auto_timevar tv (TV_ANALYZER_DUMP);
1634 0 : pretty_printer pp;
1635 0 : pp_printf (&pp, "%s.%i.%s.epath.txt",
1636 0 : dump_base_name, sd.get_index (), sd.m_d->get_kind ());
1637 0 : char *filename = xstrdup (pp_formatted_text (&pp));
1638 0 : epath->dump_to_file (filename, eg.get_ext_state ());
1639 0 : inform (loc, "exploded path written to %qs", filename);
1640 0 : free (filename);
1641 0 : }
1642 : }
1643 4161 : }
1644 :
1645 : const diagnostics::logical_locations::manager &
1646 4161 : diagnostic_manager::get_logical_location_manager () const
1647 : {
1648 4161 : gcc_assert (global_dc);
1649 4161 : auto mgr = global_dc->get_logical_location_manager ();
1650 4161 : gcc_assert (mgr);
1651 4161 : return *mgr;
1652 : }
1653 :
1654 : /* Dump C to this logger, indenting each line by the current
1655 : indentation level. */
1656 :
1657 : void
1658 0 : logger::log_canvas (const text_art::canvas &c)
1659 : {
1660 0 : std::string per_line_prefix (m_indent_level, ' ');
1661 0 : c.print_to_pp (get_printer (), per_line_prefix.c_str ());
1662 0 : pp_flush (get_printer ());
1663 0 : }
1664 :
1665 : /* Dump OBJ to LOGGER, using OBJ's make_dump_widget member function. */
1666 :
1667 : template <typename T>
1668 : void
1669 26475 : dump_to_logger (const T &obj,
1670 : logger *logger,
1671 : const char *label)
1672 : {
1673 26475 : if (!logger)
1674 26475 : return;
1675 34 : text_art::theme *theme = global_dc->get_diagram_theme ();
1676 34 : if (!theme)
1677 : return;
1678 :
1679 0 : logger->log ("%s:", label);
1680 0 : logger->inc_indent ();
1681 :
1682 0 : text_art::style_manager sm;
1683 0 : text_art::style tree_style (text_art::get_style_from_color_cap_name ("note"));
1684 :
1685 0 : text_art::style::id_t tree_style_id (sm.get_or_create_id (tree_style));
1686 :
1687 0 : text_art::dump_widget_info dwi (sm, *theme, tree_style_id);
1688 0 : if (auto w = obj.make_dump_widget (dwi))
1689 : {
1690 0 : text_art::canvas c (w->to_canvas (dwi.m_sm));
1691 0 : logger->log_canvas (c);
1692 0 : }
1693 :
1694 0 : logger->dec_indent ();
1695 0 : }
1696 :
1697 : static void
1698 26475 : log_region_model (logger *logger,
1699 : const char *label,
1700 : const region_model &model)
1701 : {
1702 13 : dump_to_logger<region_model> (model, logger, label);
1703 15 : }
1704 :
1705 36383 : class epath_rewind_context : public rewind_context
1706 : {
1707 : public:
1708 36383 : epath_rewind_context (logger *logger,
1709 : diagnostic_state input_state,
1710 : state_transition *&last_state_transition,
1711 : exploded_path::element_t &epath_element,
1712 : const region_model &src_model,
1713 : const region_model &dst_model)
1714 36383 : : rewind_context (logger, input_state),
1715 36383 : m_last_state_transition (last_state_transition),
1716 36383 : m_epath_element (epath_element),
1717 36383 : m_src_model (src_model),
1718 36383 : m_dst_model (dst_model)
1719 : {
1720 36383 : }
1721 :
1722 : const region_model &
1723 865 : get_src_region_model () const final override
1724 : {
1725 865 : return m_src_model;
1726 : }
1727 :
1728 : const region_model &
1729 2835 : get_dst_region_model () const final override
1730 : {
1731 2835 : return m_dst_model;
1732 : }
1733 :
1734 : bool
1735 10987 : could_be_affected_by_write_p (tree lhs) final override
1736 : {
1737 10987 : if (!m_input.m_region_holding_value)
1738 : return false;
1739 :
1740 431 : if (TREE_CODE (lhs) == SSA_NAME)
1741 375 : if (tree decl = m_input.m_region_holding_value->maybe_get_decl ())
1742 212 : return decl == lhs;
1743 :
1744 : return true;
1745 : }
1746 :
1747 : void
1748 79 : add_state_transition (std::unique_ptr<state_transition> st) final override
1749 : {
1750 79 : gcc_assert (st.get ());
1751 79 : if (m_logger)
1752 : {
1753 0 : m_logger->start_log_line ();
1754 0 : m_logger->log_partial ("adding state transition: ");
1755 0 : st->dump_to_pp (m_logger->get_printer ());
1756 0 : m_logger->end_log_line ();
1757 : }
1758 :
1759 : /* Chain up the state_transition instances, so that each state transition
1760 : has a pointer to the one that occurred before it (but was created after
1761 : it, since we are rewinding the epath). */
1762 79 : if (m_last_state_transition)
1763 32 : m_last_state_transition->m_prev_state_transition = st.get ();
1764 79 : m_last_state_transition = st.get ();
1765 :
1766 79 : m_epath_element.m_state_transition = std::move (st);
1767 79 : }
1768 :
1769 : private:
1770 : state_transition *&m_last_state_transition;
1771 : exploded_path::element_t &m_epath_element;
1772 : const region_model &m_src_model;
1773 : const region_model &m_dst_model;
1774 : };
1775 :
1776 : /* Return a region_model for the state after any operation/custom_edge_info
1777 : on EEDGE, but without state purging or state merging. Use SRC_MODEL as
1778 : the source state.
1779 :
1780 : We do this to make it easier to rewind state transitions in
1781 : diagnostic_manager::annotate_exploded_path. */
1782 :
1783 : static region_model
1784 43235 : make_raw_dst_region_model (logger *logger,
1785 : const exploded_edge *eedge,
1786 : const region_model &src_model,
1787 : const supergraph &sg)
1788 : {
1789 43235 : LOG_SCOPE (logger);
1790 :
1791 43235 : region_model_context *const ctxt = nullptr;
1792 :
1793 43235 : if (logger)
1794 : {
1795 13 : log_region_model (logger, "src_model", src_model);
1796 13 : log_region_model (logger, "dst_model",
1797 13 : *eedge->m_dest->get_state ().m_region_model);
1798 : }
1799 :
1800 43235 : if (eedge->m_custom_info)
1801 : {
1802 2487 : if (logger)
1803 : {
1804 0 : logger->start_log_line ();
1805 0 : eedge->m_custom_info->print (logger->get_printer ());
1806 0 : logger->end_log_line ();
1807 : }
1808 2487 : region_model new_model (src_model);
1809 2487 : eedge->m_custom_info->update_model (&new_model, eedge, ctxt);
1810 2487 : if (logger)
1811 0 : log_region_model (logger, "new model after custom_info", new_model);
1812 : return new_model;
1813 : }
1814 : else
1815 : {
1816 40748 : const superedge *sedge = eedge->m_sedge;
1817 40748 : if (sedge)
1818 : {
1819 36410 : if (logger)
1820 : {
1821 11 : label_text desc (sedge->get_description (false));
1822 11 : logger->log (" sedge: SN:%i -> SN:%i %s",
1823 11 : sedge->m_src->m_id,
1824 11 : sedge->m_dest->m_id,
1825 : desc.get ());
1826 11 : }
1827 :
1828 36410 : if (auto op = sedge->get_op ())
1829 : {
1830 26447 : if (logger)
1831 : {
1832 6 : logger->start_log_line ();
1833 6 : op->print_as_edge_label (logger->get_printer (), false);
1834 6 : logger->end_log_line ();
1835 : }
1836 26447 : feasibility_state fs (src_model, sg);
1837 26447 : op->execute_for_feasibility (*eedge,
1838 : fs,
1839 : ctxt,
1840 : nullptr);
1841 26447 : log_region_model (logger, "after operation, fs model",
1842 26447 : fs.get_model ());
1843 26447 : return fs.get_model ();
1844 26447 : }
1845 : else
1846 : {
1847 9963 : if (logger)
1848 5 : logger->log ("null operation, using src_model");
1849 9963 : return src_model;
1850 : }
1851 : }
1852 : else
1853 : {
1854 : /* Special-case the initial eedge from the origin node to the
1855 : initial function by pushing a frame for it. */
1856 4338 : if (eedge->m_src->m_index == 0)
1857 : {
1858 4049 : function *fun = eedge->m_dest->get_function ();
1859 4049 : gcc_assert (fun);
1860 4049 : region_model new_model (src_model);
1861 4049 : new_model.push_frame (*fun, nullptr, nullptr, ctxt);
1862 4049 : if (logger)
1863 : {
1864 2 : logger->log (" pushing frame for %qD", fun->decl);
1865 2 : log_region_model (logger, "new model", new_model);
1866 : }
1867 4049 : return new_model;
1868 4049 : }
1869 : }
1870 : }
1871 :
1872 289 : return src_model;
1873 43235 : }
1874 :
1875 : /* Populate the elements of EPATH with diagnostic_state and state_transition
1876 : information pertinent to the pending diagnostic. */
1877 :
1878 : void
1879 4161 : diagnostic_manager::annotate_exploded_path (const path_builder &pb,
1880 : exploded_path &epath) const
1881 : {
1882 4161 : auto logger = get_logger ();
1883 4161 : LOG_SCOPE (logger);
1884 :
1885 : // TODO: consolidate this with build_emission_path?
1886 4161 : interesting_t interest;
1887 4161 : pb.get_pending_diagnostic ()->mark_interesting_stuff (&interest);
1888 :
1889 4161 : gcc_assert (epath.m_elements.size () > 0);
1890 :
1891 4161 : diagnostic_state curr_state;
1892 4161 : state_transition *last_state_transition = nullptr;
1893 :
1894 4161 : if (interest.m_read_regions.size () > 0)
1895 599 : curr_state = interest.m_read_regions[0];
1896 :
1897 : /* Walk EPATH forwards, generating region_model instances for the elements
1898 : of EPATH without state purging or merging, so that we can reliably
1899 : rewind state. */
1900 4161 : std::vector<region_model> src_models;
1901 4161 : std::vector<region_model> dst_models;
1902 47396 : for (int idx = 0; idx < epath.m_elements.size (); ++idx)
1903 : {
1904 43235 : auto eedge = epath.m_elements[idx].m_eedge;
1905 43235 : if (logger)
1906 13 : logger->log ("edge[%i]: considering EN %i -> EN %i",
1907 : idx,
1908 13 : eedge->m_src->m_index,
1909 13 : eedge->m_dest->m_index);
1910 43235 : region_model src_model (pb.get_ext_state ().get_model_manager ());
1911 43235 : if (idx > 0)
1912 39074 : src_model = dst_models[idx - 1];
1913 43235 : src_models.push_back (src_model);
1914 43235 : dst_models.push_back
1915 43235 : (make_raw_dst_region_model (logger,
1916 : eedge,
1917 : src_model,
1918 : pb.get_supergraph ()));
1919 43235 : }
1920 :
1921 : /* Walk EPATH backwards, using the region_models we just built,
1922 : propagating annotation information backwards. */
1923 39960 : for (int idx = epath.m_elements.size () - 1; idx >= 0; --idx)
1924 : {
1925 36383 : exploded_path::element_t &iter_element = epath.m_elements[idx];
1926 36383 : if (logger)
1927 : {
1928 11 : logger->log ("edge[%i]: considering rewinding EN %i -> EN %i",
1929 : idx,
1930 11 : iter_element.m_eedge->m_src->m_index,
1931 11 : iter_element.m_eedge->m_dest->m_index);
1932 11 : logger->start_log_line ();
1933 11 : logger->log_partial ("curr_state: ");
1934 11 : curr_state.dump_to_pp (logger->get_printer ());
1935 11 : logger->end_log_line ();
1936 : }
1937 36383 : iter_element.m_state_at_dst = curr_state;
1938 36383 : const exploded_edge *eedge = iter_element.m_eedge;
1939 36383 : gcc_assert (eedge);
1940 :
1941 36383 : epath_rewind_context ctxt (logger, curr_state,
1942 : last_state_transition, iter_element,
1943 36383 : src_models[idx], dst_models[idx]);
1944 36383 : if (eedge->m_custom_info)
1945 : {
1946 2126 : if (logger)
1947 : {
1948 0 : logger->start_log_line ();
1949 0 : logger->log_partial ("custom_edge_info: ");
1950 0 : eedge->m_custom_info->print (logger->get_printer ());
1951 0 : logger->end_log_line ();
1952 : }
1953 2126 : if (!eedge->m_custom_info->try_to_rewind_data_flow (ctxt))
1954 : {
1955 474 : if (logger)
1956 0 : logger->log ("could not rewind custom info");
1957 474 : return;
1958 : }
1959 : }
1960 34257 : else if (const operation *op = eedge->maybe_get_op ())
1961 : {
1962 21601 : if (logger)
1963 : {
1964 6 : logger->start_log_line ();
1965 6 : logger->log_partial ("op: ");
1966 6 : op->print_as_edge_label (logger->get_printer (), false);
1967 6 : logger->end_log_line ();
1968 : }
1969 21601 : if (!op->try_to_rewind_data_flow (ctxt))
1970 : {
1971 110 : if (logger)
1972 1 : logger->log ("could not rewind op");
1973 110 : return;
1974 : }
1975 : }
1976 :
1977 35799 : iter_element.m_state_at_src = ctxt.m_output;
1978 35799 : curr_state = ctxt.m_output;
1979 35799 : if (logger)
1980 : {
1981 10 : logger->log ("rewound");
1982 10 : logger->start_log_line ();
1983 10 : logger->log_partial ("curr_state: ");
1984 10 : curr_state.dump_to_pp (logger->get_printer ());
1985 10 : logger->end_log_line ();
1986 : }
1987 35799 : }
1988 8322 : }
1989 :
1990 : /* Emit a "path" of events to EMISSION_PATH describing the exploded path
1991 : EPATH within EG. */
1992 :
1993 : void
1994 4161 : diagnostic_manager::build_emission_path (const path_builder &pb,
1995 : const exploded_path &epath,
1996 : checker_path *emission_path) const
1997 : {
1998 4161 : LOG_SCOPE (get_logger ());
1999 :
2000 4161 : interesting_t interest;
2001 4161 : pb.get_pending_diagnostic ()->mark_interesting_stuff (&interest);
2002 :
2003 : /* Add region creation events for any globals of interest, at the
2004 : beginning of the path. */
2005 4161 : {
2006 8110 : for (auto reg : interest.m_region_creation)
2007 1335 : switch (reg->get_memory_space ())
2008 : {
2009 1119 : default:
2010 1119 : continue;
2011 216 : case MEMSPACE_CODE:
2012 216 : case MEMSPACE_GLOBALS:
2013 216 : case MEMSPACE_READONLY_DATA:
2014 216 : {
2015 216 : const region *base_reg = reg->get_base_region ();
2016 216 : if (tree decl = base_reg->maybe_get_decl ())
2017 207 : if (DECL_P (decl)
2018 207 : && useful_location_p (DECL_SOURCE_LOCATION (decl)))
2019 : {
2020 207 : emission_path->add_region_creation_events
2021 207 : (pb.get_pending_diagnostic (),
2022 : reg, nullptr,
2023 207 : event_loc_info (DECL_SOURCE_LOCATION (decl),
2024 : NULL_TREE,
2025 207 : 0),
2026 207 : m_verbosity > 3);
2027 : }
2028 : }
2029 1119 : }
2030 : }
2031 :
2032 : /* Walk EPATH, adding events as appropriate. */
2033 47396 : for (unsigned i = 0; i < epath.m_elements.size (); ++i)
2034 : {
2035 43235 : const exploded_edge *eedge = epath.m_elements[i].m_eedge;
2036 43235 : gcc_assert (eedge);
2037 43235 : add_events_for_eedge (pb, *eedge, emission_path, &interest,
2038 43235 : epath.m_elements[i].m_state_transition.get ());
2039 : }
2040 4161 : add_event_on_final_node (pb, epath.get_final_enode (),
2041 : emission_path, &interest);
2042 4161 : }
2043 :
2044 : /* Emit a region_creation_event when requested on the last statement in
2045 : the path.
2046 :
2047 : If a region_creation_event should be emitted on the last statement of the
2048 : path, we need to peek to the successors to get whether the final enode
2049 : created a region.
2050 : */
2051 :
2052 : void
2053 4161 : diagnostic_manager::add_event_on_final_node (const path_builder &pb,
2054 : const exploded_node *final_enode,
2055 : checker_path *emission_path,
2056 : interesting_t *interest) const
2057 : {
2058 4161 : const program_point &src_point = final_enode->get_point ();
2059 4161 : const int src_stack_depth = src_point.get_stack_depth ();
2060 4161 : const program_state &src_state = final_enode->get_state ();
2061 4161 : const region_model *src_model = src_state.m_region_model;
2062 :
2063 4161 : unsigned j;
2064 4161 : exploded_edge *e;
2065 7473 : FOR_EACH_VEC_ELT (final_enode->m_succs, j, e)
2066 : {
2067 3389 : exploded_node *dst = e->m_dest;
2068 3389 : const program_state &dst_state = dst->get_state ();
2069 3389 : const region_model *dst_model = dst_state.m_region_model;
2070 3389 : if (src_model->get_dynamic_extents ()
2071 3389 : != dst_model->get_dynamic_extents ())
2072 : {
2073 : unsigned i;
2074 : const region *reg;
2075 : bool emitted = false;
2076 456 : FOR_EACH_VEC_ELT (interest->m_region_creation, i, reg)
2077 : {
2078 92 : const region *base_reg = reg->get_base_region ();
2079 92 : const svalue *old_extents
2080 92 : = src_model->get_dynamic_extents (base_reg);
2081 92 : const svalue *new_extents
2082 92 : = dst_model->get_dynamic_extents (base_reg);
2083 92 : if (old_extents == nullptr && new_extents != nullptr)
2084 81 : switch (base_reg->get_kind ())
2085 : {
2086 : default:
2087 : break;
2088 77 : case RK_HEAP_ALLOCATED:
2089 77 : case RK_ALLOCA:
2090 77 : emission_path->add_region_creation_events
2091 154 : (pb.get_pending_diagnostic (),
2092 : reg,
2093 : dst_model,
2094 77 : event_loc_info (src_point.get_location (),
2095 : src_point.get_fndecl (),
2096 154 : src_stack_depth),
2097 : false);
2098 77 : emitted = true;
2099 77 : break;
2100 : }
2101 : }
2102 364 : if (emitted)
2103 : break;
2104 : }
2105 : }
2106 4161 : }
2107 :
2108 : /* Subclass of state_change_visitor that creates state_change_event
2109 : instances. */
2110 :
2111 43239 : class state_change_event_creator : public state_change_visitor
2112 : {
2113 : public:
2114 43239 : state_change_event_creator (const path_builder &pb,
2115 : const exploded_edge &eedge,
2116 : checker_path *emission_path)
2117 43239 : : m_pb (pb),
2118 43239 : m_eedge (eedge),
2119 43239 : m_emission_path (emission_path)
2120 : {}
2121 :
2122 98 : bool on_global_state_change (const state_machine &sm,
2123 : state_machine::state_t src_sm_val,
2124 : state_machine::state_t dst_sm_val)
2125 : final override
2126 : {
2127 98 : if (&sm != m_pb.get_sm ())
2128 : return false;
2129 55 : const exploded_node *src_node = m_eedge.m_src;
2130 55 : const exploded_node *dst_node = m_eedge.m_dest;
2131 55 : const gimple *stmt = m_eedge.maybe_get_stmt ();
2132 55 : const program_state &dst_state = dst_node->get_state ();
2133 :
2134 55 : m_emission_path->add_event
2135 55 : (std::make_unique<state_change_event> (m_eedge.m_src,
2136 : stmt,
2137 : sm,
2138 110 : nullptr,
2139 : src_sm_val,
2140 : dst_sm_val,
2141 55 : nullptr,
2142 : dst_state,
2143 : src_node));
2144 55 : return false;
2145 : }
2146 :
2147 4405 : bool on_state_change (const state_machine &sm,
2148 : state_machine::state_t src_sm_val,
2149 : state_machine::state_t dst_sm_val,
2150 : const svalue *sval,
2151 : const svalue *dst_origin_sval) final override
2152 : {
2153 4405 : if (&sm != m_pb.get_sm ())
2154 : return false;
2155 :
2156 3347 : const exploded_node *src_node = m_eedge.m_src;
2157 3347 : const exploded_node *dst_node = m_eedge.m_dest;
2158 3347 : const gimple *stmt = m_eedge.maybe_get_stmt ();
2159 3347 : const program_state &dst_state = dst_node->get_state ();
2160 :
2161 3347 : m_emission_path->add_event
2162 3347 : (std::make_unique<state_change_event> (m_eedge.m_src,
2163 : stmt,
2164 : sm,
2165 : sval,
2166 : src_sm_val,
2167 : dst_sm_val,
2168 : dst_origin_sval,
2169 : dst_state,
2170 : src_node));
2171 3347 : return false;
2172 : }
2173 :
2174 : const path_builder &m_pb;
2175 : const exploded_edge &m_eedge;
2176 : checker_path *m_emission_path;
2177 : };
2178 :
2179 : /* Compare SRC_STATE and DST_STATE (which use EXT_STATE), and call
2180 : VISITOR's on_state_change for every sm-state change that occurs
2181 : to a tree, and on_global_state_change for every global state change
2182 : that occurs.
2183 :
2184 : This determines the state changes that ought to be reported to
2185 : the user: a combination of the effects of changes to sm_state_map
2186 : (which maps svalues to sm-states), and of region_model changes
2187 : (which map trees to svalues).
2188 :
2189 : Bail out early and return true if any call to on_global_state_change
2190 : or on_state_change returns true, otherwise return false.
2191 :
2192 : This is split out to make it easier to experiment with changes to
2193 : exploded_node granularity (so that we can observe what state changes
2194 : lead to state_change_events being emitted). */
2195 :
2196 : bool
2197 43239 : for_each_state_change (const program_state &src_state,
2198 : const program_state &dst_state,
2199 : const extrinsic_state &ext_state,
2200 : state_change_visitor *visitor)
2201 : {
2202 86478 : gcc_assert (src_state.m_checker_states.length ()
2203 : == ext_state.get_num_checkers ());
2204 86478 : gcc_assert (dst_state.m_checker_states.length ()
2205 : == ext_state.get_num_checkers ());
2206 344380 : for (unsigned i = 0; i < ext_state.get_num_checkers (); i++)
2207 : {
2208 301141 : const state_machine &sm = ext_state.get_sm (i);
2209 301141 : const sm_state_map &src_smap = *src_state.m_checker_states[i];
2210 301141 : const sm_state_map &dst_smap = *dst_state.m_checker_states[i];
2211 :
2212 : /* Add events for any global state changes. */
2213 301141 : if (src_smap.get_global_state () != dst_smap.get_global_state ())
2214 98 : if (visitor->on_global_state_change (sm,
2215 : src_smap.get_global_state (),
2216 : dst_smap.get_global_state ()))
2217 : return true;
2218 :
2219 : /* Add events for per-svalue state changes. */
2220 336647 : for (sm_state_map::iterator_t iter = dst_smap.begin ();
2221 637788 : iter != dst_smap.end ();
2222 35506 : ++iter)
2223 : {
2224 35506 : const svalue *sval = (*iter).first;
2225 35506 : state_machine::state_t dst_sm_val = (*iter).second.m_state;
2226 35506 : state_machine::state_t src_sm_val
2227 35506 : = src_smap.get_state (sval, ext_state);
2228 35506 : if (dst_sm_val != src_sm_val)
2229 : {
2230 4405 : const svalue *origin_sval = (*iter).second.m_origin;
2231 4405 : if (visitor->on_state_change (sm, src_sm_val, dst_sm_val,
2232 : sval, origin_sval))
2233 0 : return true;
2234 : }
2235 : }
2236 : }
2237 : return false;
2238 : }
2239 :
2240 : /* Subroutine of diagnostic_manager::build_emission_path.
2241 : Add any events for EEDGE to EMISSION_PATH. */
2242 :
2243 : void
2244 43239 : diagnostic_manager::add_events_for_eedge (const path_builder &pb,
2245 : const exploded_edge &eedge,
2246 : checker_path *emission_path,
2247 : interesting_t *interest,
2248 : const state_transition *state_trans) const
2249 : {
2250 43239 : const exploded_node *src_node = eedge.m_src;
2251 43239 : const program_point &src_point = src_node->get_point ();
2252 43239 : const int src_stack_depth = src_point.get_stack_depth ();
2253 43239 : const exploded_node *dst_node = eedge.m_dest;
2254 43239 : const program_point &dst_point = dst_node->get_point ();
2255 43239 : const int dst_stack_depth = dst_point.get_stack_depth ();
2256 43239 : if (get_logger ())
2257 : {
2258 13 : get_logger ()->start_log_line ();
2259 13 : pretty_printer *pp = get_logger ()->get_printer ();
2260 13 : pp_printf (pp, "EN %i -> EN %i: ",
2261 13 : eedge.m_src->m_index,
2262 13 : eedge.m_dest->m_index);
2263 13 : src_point.print (pp, format (false));
2264 13 : pp_string (pp, "-> ");
2265 13 : dst_point.print (pp, format (false));
2266 13 : if (state_trans)
2267 : {
2268 0 : pp_string (pp, " {");
2269 0 : state_trans->dump_to_pp (pp);
2270 0 : pp_string (pp, "}");
2271 : }
2272 13 : get_logger ()->end_log_line ();
2273 : }
2274 43239 : const program_state &src_state = src_node->get_state ();
2275 43239 : const program_state &dst_state = dst_node->get_state ();
2276 :
2277 43239 : bool created_event_for_state_trans = false;
2278 :
2279 : /* Add state change events for the states that have changed.
2280 : We add these before events for superedges, so that if we have a
2281 : state_change_event due to following an edge, we'll get this sequence
2282 : of events:
2283 :
2284 : | if (!ptr)
2285 : | ~
2286 : | |
2287 : | (1) assuming 'ptr' is non-NULL (state_change_event)
2288 : | (2) following 'false' branch... (start_cfg_edge_event)
2289 : ...
2290 : | do_something (ptr);
2291 : | ~~~~~~~~~~~~~^~~~~
2292 : | |
2293 : | (3) ...to here (end_cfg_edge_event). */
2294 43239 : state_change_event_creator visitor (pb, eedge, emission_path);
2295 43239 : for_each_state_change (src_state, dst_state, pb.get_ext_state (),
2296 : &visitor);
2297 :
2298 : /* Give diagnostics an opportunity to inject extra events, or
2299 : to override the rest of this function. */
2300 43239 : pending_diagnostic *pd = pb.get_pending_diagnostic ();
2301 43239 : if (pd->maybe_add_custom_events_for_eedge (eedge, emission_path))
2302 : return;
2303 :
2304 : /* Allow non-standard edges to add events, e.g. when rewinding from
2305 : longjmp to a setjmp. */
2306 42262 : if (eedge.m_custom_info)
2307 : {
2308 2485 : eedge.m_custom_info->add_events_to_path (emission_path, eedge, *pd,
2309 : state_trans);
2310 2485 : created_event_for_state_trans = true;
2311 : }
2312 :
2313 : /* Don't add events for insignificant edges at verbosity levels below 3. */
2314 42262 : if (m_verbosity < 3)
2315 42037 : if (!significant_edge_p (pb, eedge) && !state_trans)
2316 : return;
2317 :
2318 : /* Add events for operations. */
2319 41460 : if (eedge.m_sedge)
2320 36058 : if (auto op = eedge.m_sedge->get_op ())
2321 26403 : op->add_any_events_for_eedge (eedge, *emission_path);
2322 :
2323 : /* Add events for function entry. */
2324 41460 : if (dst_point.get_supernode ()->entry_p ())
2325 : {
2326 5322 : pb.get_pending_diagnostic ()->add_function_entry_event
2327 5322 : (eedge, emission_path,
2328 5322 : (state_trans
2329 23 : ? state_trans->dyn_cast_state_transition_at_call ()
2330 : : nullptr));
2331 5322 : created_event_for_state_trans = true;
2332 : /* Create region_creation_events for on-stack regions within
2333 : this frame. */
2334 5322 : if (interest)
2335 : {
2336 : unsigned i;
2337 : const region *reg;
2338 6797 : FOR_EACH_VEC_ELT (interest->m_region_creation, i, reg)
2339 1475 : if (const frame_region *frame = reg->maybe_get_frame_region ())
2340 1962 : if (frame->get_fndecl () == dst_point.get_fndecl ())
2341 : {
2342 862 : const region *base_reg = reg->get_base_region ();
2343 862 : if (tree decl = base_reg->maybe_get_decl ())
2344 761 : if (DECL_P (decl)
2345 761 : && useful_location_p (DECL_SOURCE_LOCATION (decl)))
2346 : {
2347 757 : emission_path->add_region_creation_events
2348 1514 : (pb.get_pending_diagnostic (),
2349 757 : reg, dst_state.m_region_model,
2350 757 : event_loc_info (DECL_SOURCE_LOCATION (decl),
2351 : dst_point.get_fndecl (),
2352 757 : dst_stack_depth),
2353 757 : m_verbosity > 3);
2354 : }
2355 : }
2356 : }
2357 : }
2358 :
2359 : /* Look for changes in dynamic extents, which will identify
2360 : the creation of heap-based regions and alloca regions. */
2361 41460 : if (interest)
2362 : {
2363 41456 : const region_model *src_model = src_state.m_region_model;
2364 41456 : const region_model *dst_model = dst_state.m_region_model;
2365 41456 : if (src_model->get_dynamic_extents ()
2366 41456 : != dst_model->get_dynamic_extents ())
2367 : {
2368 : unsigned i;
2369 : const region *reg;
2370 2395 : FOR_EACH_VEC_ELT (interest->m_region_creation, i, reg)
2371 : {
2372 421 : const region *base_reg = reg->get_base_region ();
2373 421 : const svalue *old_extents
2374 421 : = src_model->get_dynamic_extents (base_reg);
2375 421 : const svalue *new_extents
2376 421 : = dst_model->get_dynamic_extents (base_reg);
2377 421 : if (old_extents == nullptr && new_extents != nullptr)
2378 277 : switch (base_reg->get_kind ())
2379 : {
2380 : default:
2381 : break;
2382 241 : case RK_HEAP_ALLOCATED:
2383 241 : case RK_ALLOCA:
2384 241 : emission_path->add_region_creation_events
2385 241 : (pb.get_pending_diagnostic (),
2386 : reg, dst_model,
2387 241 : event_loc_info (src_point.get_location (),
2388 : src_point.get_fndecl (),
2389 482 : src_stack_depth),
2390 241 : m_verbosity > 3);
2391 241 : break;
2392 : }
2393 : }
2394 : }
2395 : }
2396 :
2397 : /* If we have a state transition and haven't yet created an
2398 : event that describes it, do so now. */
2399 41460 : if (state_trans && !created_event_for_state_trans)
2400 44 : emission_path->add_event
2401 44 : (std::make_unique<state_transition_event>
2402 44 : (eedge.m_src->get_point (),
2403 : state_trans));
2404 :
2405 41460 : if (pb.get_feasibility_problem ()
2406 41460 : && &pb.get_feasibility_problem ()->m_eedge == &eedge)
2407 : {
2408 4 : pretty_printer pp;
2409 4 : pp_format_decoder (&pp) = default_tree_printer;
2410 4 : pp_string (&pp,
2411 : "this path would have been rejected as infeasible"
2412 : " at this edge: ");
2413 4 : pb.get_feasibility_problem ()->dump_to_pp (&pp);
2414 4 : emission_path->add_event
2415 4 : (std::make_unique<precanned_custom_event>
2416 4 : (event_loc_info (dst_point.get_location (),
2417 : dst_point.get_fndecl (),
2418 8 : dst_stack_depth),
2419 4 : pp_formatted_text (&pp)));
2420 4 : }
2421 43239 : }
2422 :
2423 : /* Return true if EEDGE is a significant edge in the path to the diagnostic
2424 : for PB.
2425 :
2426 : Consider all of the sibling out-eedges from the same source enode
2427 : as EEDGE.
2428 : If there's no path from the destinations of those eedges to the
2429 : diagnostic enode, then we have to take this eedge and thus it's
2430 : significant.
2431 :
2432 : Conversely if there is a path from the destination of any other sibling
2433 : eedge to the diagnostic enode, then this edge is insignificant.
2434 :
2435 : Example 1: redundant if-else:
2436 :
2437 : (A) if (...) A
2438 : (B) ... / \
2439 : else B C
2440 : (C) ... \ /
2441 : (D) [DIAGNOSTIC] D
2442 :
2443 : D is reachable by either B or C, so neither of these edges
2444 : are significant.
2445 :
2446 : Example 2: pertinent if-else:
2447 :
2448 : (A) if (...) A
2449 : (B) ... / \
2450 : else B C
2451 : (C) [NECESSARY CONDITION] | |
2452 : (D) [POSSIBLE DIAGNOSTIC] D1 D2
2453 :
2454 : D becomes D1 and D2 in the exploded graph, where the diagnostic occurs
2455 : at D2. D2 is only reachable via C, so the A -> C edge is significant.
2456 :
2457 : Example 3: redundant loop:
2458 :
2459 : (A) while (...) +-->A
2460 : (B) ... | / \
2461 : (C) ... +-B C
2462 : (D) [DIAGNOSTIC] |
2463 : D
2464 :
2465 : D is reachable from both B and C, so the A->C edge is not significant. */
2466 :
2467 : bool
2468 42037 : diagnostic_manager::significant_edge_p (const path_builder &pb,
2469 : const exploded_edge &eedge) const
2470 : {
2471 42037 : int i;
2472 42037 : exploded_edge *sibling;
2473 115711 : FOR_EACH_VEC_ELT (eedge.m_src->m_succs, i, sibling)
2474 : {
2475 74476 : if (sibling == &eedge)
2476 41656 : continue;
2477 32820 : if (pb.reachable_from_p (sibling->m_dest))
2478 : {
2479 802 : if (get_logger ())
2480 0 : get_logger ()->log (" edge EN: %i -> EN: %i is insignificant as"
2481 : " EN: %i is also reachable via"
2482 : " EN: %i -> EN: %i",
2483 0 : eedge.m_src->m_index, eedge.m_dest->m_index,
2484 0 : pb.get_diag_node ()->m_index,
2485 0 : sibling->m_src->m_index,
2486 : sibling->m_dest->m_index);
2487 802 : return false;
2488 : }
2489 : }
2490 :
2491 : return true;
2492 : }
2493 :
2494 : /* Prune PATH, based on the verbosity level, to the most pertinent
2495 : events for a diagnostic that involves VAR ending in state STATE
2496 : (for state machine SM).
2497 :
2498 : PATH is updated in place, and the redundant checker_events are deleted.
2499 :
2500 : As well as deleting events, call record_critical_state on events in
2501 : which state critical to the pending_diagnostic is being handled; see
2502 : the comment for diagnostic_manager::prune_for_sm_diagnostic. */
2503 :
2504 : void
2505 4161 : diagnostic_manager::prune_path (checker_path *path,
2506 : const state_machine *sm,
2507 : const svalue *sval,
2508 : state_machine::state_t state) const
2509 : {
2510 4161 : LOG_FUNC (get_logger ());
2511 4161 : path->maybe_log (get_logger (), "path");
2512 4161 : prune_for_sm_diagnostic (path, sm, sval, state);
2513 4161 : prune_interproc_events (path);
2514 4161 : if (! flag_analyzer_show_events_in_system_headers)
2515 4159 : prune_system_headers (path);
2516 4161 : consolidate_conditions (path);
2517 4161 : consolidate_unwind_events (path);
2518 4161 : finish_pruning (path);
2519 4161 : path->maybe_log (get_logger (), "pruned");
2520 4161 : }
2521 :
2522 : /* A cheap test to determine if EXPR can be the expression of interest in
2523 : an sm-diagnostic, so that we can reject cases where we have a non-lvalue.
2524 : We don't have always have a model when calling this, so we can't use
2525 : tentative_region_model_context, so there can be false positives. */
2526 :
2527 : static bool
2528 0 : can_be_expr_of_interest_p (tree expr)
2529 : {
2530 0 : if (!expr)
2531 : return false;
2532 :
2533 : /* Reject constants. */
2534 0 : if (CONSTANT_CLASS_P (expr))
2535 0 : return false;
2536 :
2537 : /* Otherwise assume that it can be an lvalue. */
2538 : return true;
2539 : }
2540 :
2541 : /* First pass of diagnostic_manager::prune_path: apply verbosity level,
2542 : pruning unrelated state change events.
2543 :
2544 : Iterate backwards through PATH, skipping state change events that aren't
2545 : VAR but update the pertinent VAR when state-copying occurs.
2546 :
2547 : As well as deleting events, call record_critical_state on events in
2548 : which state critical to the pending_diagnostic is being handled, so
2549 : that the event's get_desc vfunc can potentially supply a more precise
2550 : description of the event to the user.
2551 : e.g. improving
2552 : "calling 'foo' from 'bar'"
2553 : to
2554 : "passing possibly-NULL pointer 'ptr' to 'foo' from 'bar' as param 1"
2555 : when the diagnostic relates to later dereferencing 'ptr'. */
2556 :
2557 : void
2558 4161 : diagnostic_manager::prune_for_sm_diagnostic (checker_path *path,
2559 : const state_machine *sm,
2560 : const svalue *sval,
2561 : state_machine::state_t state) const
2562 : {
2563 4161 : int idx = path->num_events () - 1;
2564 69535 : while (idx >= 0 && idx < (signed)path->num_events ())
2565 : {
2566 32687 : checker_event *base_event = path->get_checker_event (idx);
2567 32687 : if (get_logger ())
2568 : {
2569 6 : if (sm)
2570 : {
2571 0 : if (sval)
2572 : {
2573 0 : label_text sval_desc = sval->get_desc ();
2574 0 : log ("considering event %i (%s), with sval: %qs, state: %qs",
2575 : idx, event_kind_to_string (base_event->get_kind ()),
2576 : sval_desc.get (), state->get_name ());
2577 0 : }
2578 : else
2579 0 : log ("considering event %i (%s), with global state: %qs",
2580 : idx, event_kind_to_string (base_event->get_kind ()),
2581 : state->get_name ());
2582 : }
2583 : else
2584 6 : log ("considering event %i", idx);
2585 : }
2586 :
2587 32687 : switch (base_event->get_kind ())
2588 : {
2589 0 : default:
2590 0 : gcc_unreachable ();
2591 :
2592 0 : case event_kind::debug:
2593 0 : if (m_verbosity < 4)
2594 : {
2595 0 : log ("filtering event %i: debug event", idx);
2596 0 : path->delete_event (idx);
2597 : }
2598 : break;
2599 :
2600 : case event_kind::custom:
2601 : /* Don't filter custom events. */
2602 : break;
2603 :
2604 13944 : case event_kind::stmt:
2605 13944 : {
2606 13944 : if (m_verbosity < 4)
2607 : {
2608 13944 : log ("filtering event %i: statement event", idx);
2609 13944 : path->delete_event (idx);
2610 : }
2611 : }
2612 : break;
2613 :
2614 : case event_kind::region_creation:
2615 : /* Don't filter these. */
2616 : break;
2617 :
2618 44 : case event_kind::state_transition:
2619 : /* Prune these if they have an empty description. */
2620 44 : {
2621 44 : tree_dump_pretty_printer pp (nullptr);
2622 44 : base_event->print_desc (pp);
2623 44 : if (strlen (pp_formatted_text (&pp)) == 0)
2624 : {
2625 0 : log (("filtering event %i:"
2626 : " state_transition_event with empty description"),
2627 : idx);
2628 0 : path->delete_event (idx);
2629 : }
2630 44 : }
2631 44 : break;
2632 :
2633 5322 : case event_kind::function_entry:
2634 5322 : if (m_verbosity < 1)
2635 : {
2636 33 : log ("filtering event %i: function entry", idx);
2637 33 : path->delete_event (idx);
2638 : }
2639 : break;
2640 :
2641 3827 : case event_kind::state_change:
2642 3827 : {
2643 3827 : state_change_event *state_change = (state_change_event *)base_event;
2644 3827 : gcc_assert (state_change->m_dst_state.m_region_model);
2645 :
2646 3827 : if (state_change->m_sval == sval)
2647 : {
2648 2188 : if (state_change->m_origin)
2649 : {
2650 0 : if (get_logger ())
2651 : {
2652 0 : label_text sval_desc = sval->get_desc ();
2653 0 : label_text origin_sval_desc
2654 0 : = state_change->m_origin->get_desc ();
2655 0 : log ("event %i:"
2656 : " switching var of interest from %qs to %qs",
2657 : idx, sval_desc.get (),
2658 : origin_sval_desc.get ());
2659 0 : }
2660 0 : sval = state_change->m_origin;
2661 : }
2662 2188 : log ("event %i: switching state of interest from %qs to %qs",
2663 2188 : idx, state_change->m_to->get_name (),
2664 2188 : state_change->m_from->get_name ());
2665 2188 : state = state_change->m_from;
2666 : }
2667 1639 : else if (m_verbosity < 4)
2668 : {
2669 1639 : if (get_logger ())
2670 : {
2671 0 : if (state_change->m_sval)
2672 : {
2673 0 : label_text change_sval_desc
2674 0 : = state_change->m_sval->get_desc ();
2675 0 : if (sval)
2676 : {
2677 0 : label_text sval_desc = sval->get_desc ();
2678 0 : log ("filtering event %i:"
2679 : " state change to %qs unrelated to %qs",
2680 : idx, change_sval_desc.get (),
2681 : sval_desc.get ());
2682 0 : }
2683 : else
2684 0 : log ("filtering event %i: state change to %qs",
2685 : idx, change_sval_desc.get ());
2686 0 : }
2687 : else
2688 0 : log ("filtering event %i: global state change", idx);
2689 : }
2690 1639 : path->delete_event (idx);
2691 : }
2692 : }
2693 : break;
2694 :
2695 2690 : case event_kind::start_cfg_edge:
2696 2690 : {
2697 2690 : cfg_edge_event *event = (cfg_edge_event *)base_event;
2698 :
2699 : /* TODO: is this edge significant to var?
2700 : See if var can be in other states in the dest, but not
2701 : in other states in the src?
2702 : Must have multiple sibling edges. */
2703 :
2704 2690 : if (event->should_filter_p (m_verbosity))
2705 : {
2706 36 : log ("filtering events %i and %i: CFG edge", idx, idx + 1);
2707 36 : path->delete_event (idx);
2708 : /* Also delete the corresponding event_kind::end_cfg_edge. */
2709 36 : gcc_assert (path->get_checker_event (idx)->get_kind ()
2710 : == event_kind::end_cfg_edge);
2711 36 : path->delete_event (idx);
2712 : }
2713 : }
2714 : break;
2715 :
2716 : case event_kind::end_cfg_edge:
2717 : /* These come in pairs with event_kind::start_cfg_edge events and are
2718 : filtered when their start event is filtered. */
2719 : break;
2720 :
2721 : case event_kind::catch_:
2722 : case event_kind::throw_:
2723 : case event_kind::unwind:
2724 : /* Don't filter these. */
2725 : break;
2726 :
2727 1241 : case event_kind::call_:
2728 1241 : {
2729 1241 : call_event *event = (call_event *)base_event;
2730 1241 : const region_model *callee_model
2731 1241 : = event->m_eedge.m_dest->get_state ().m_region_model;
2732 1241 : const region_model *caller_model
2733 1241 : = event->m_eedge.m_src->get_state ().m_region_model;
2734 1241 : tree callee_var = callee_model->get_representative_tree (sval);
2735 1241 : callsite_expr expr;
2736 :
2737 1241 : tree caller_var;
2738 1241 : if (auto op = event->get_call_and_return_op ())
2739 : {
2740 1241 : tree callee_fndecl
2741 1241 : = event->m_eedge.m_dest->get_point ().get_fndecl ();
2742 1241 : caller_var
2743 1241 : = op->map_expr_from_callee_to_caller (callee_fndecl,
2744 : callee_var,
2745 : &expr);
2746 : }
2747 : else
2748 0 : caller_var = caller_model->get_representative_tree (sval);
2749 :
2750 1241 : if (caller_var)
2751 : {
2752 218 : if (get_logger ())
2753 : {
2754 0 : label_text sval_desc = sval->get_desc ();
2755 0 : log ("event %i:"
2756 : " recording critical state for %qs at call"
2757 : " from %qE in callee to %qE in caller",
2758 : idx, sval_desc.get (), callee_var, caller_var);
2759 0 : }
2760 218 : if (expr.param_p ())
2761 218 : event->record_critical_state (caller_var, state);
2762 : }
2763 : }
2764 1241 : break;
2765 :
2766 631 : case event_kind::return_:
2767 631 : {
2768 631 : if (sval)
2769 : {
2770 443 : return_event *event = (return_event *)base_event;
2771 443 : const region_model *caller_model
2772 443 : = event->m_eedge.m_dest->get_state ().m_region_model;
2773 443 : tree caller_var = caller_model->get_representative_tree (sval);
2774 443 : const region_model *callee_model
2775 443 : = event->m_eedge.m_src->get_state ().m_region_model;
2776 443 : callsite_expr expr;
2777 :
2778 443 : tree callee_var;
2779 :
2780 443 : if (auto op = event->get_call_and_return_op ())
2781 : {
2782 443 : tree callee_fndecl
2783 443 : = event->m_eedge.m_src->get_point ().get_fndecl ();
2784 443 : callee_var
2785 443 : = op->map_expr_from_caller_to_callee (callee_fndecl,
2786 : caller_var,
2787 : &expr);
2788 : }
2789 : else
2790 0 : callee_var = callee_model->get_representative_tree (sval);
2791 :
2792 443 : if (callee_var)
2793 : {
2794 185 : if (get_logger ())
2795 : {
2796 0 : label_text sval_desc = sval->get_desc ();
2797 0 : log ("event %i:"
2798 : " recording critical state for %qs at return"
2799 : " from %qE in caller to %qE in callee",
2800 : idx, sval_desc.get (), callee_var, callee_var);
2801 0 : }
2802 185 : if (expr.return_value_p ())
2803 94 : event->record_critical_state (callee_var, state);
2804 : }
2805 : }
2806 : }
2807 : break;
2808 :
2809 : case event_kind::inlined_call:
2810 : /* We don't expect to see these yet, as they're added later.
2811 : We'd want to keep them around. */
2812 : break;
2813 :
2814 : case event_kind::setjmp_:
2815 : /* TODO: only show setjmp_events that matter i.e. those for which
2816 : there is a later rewind event using them. */
2817 : case event_kind::rewind_from_longjmp:
2818 : case event_kind::rewind_to_setjmp:
2819 : break;
2820 :
2821 : case event_kind::warning:
2822 : /* Always show the final "warning" event in the path. */
2823 : break;
2824 : }
2825 32687 : idx--;
2826 : }
2827 4161 : }
2828 :
2829 : /* Subroutine of diagnostic_manager::prune_for_sm_diagnostic.
2830 : If *EXPR is not suitable to be the expression of interest in
2831 : an sm-diagnostic, set *EXPR to NULL and log. */
2832 :
2833 : void
2834 0 : diagnostic_manager::update_for_unsuitable_sm_exprs (tree *expr) const
2835 : {
2836 0 : gcc_assert (expr);
2837 0 : if (*expr && !can_be_expr_of_interest_p (*expr))
2838 : {
2839 0 : log ("new var %qE is unsuitable; setting var to NULL", *expr);
2840 0 : *expr = NULL_TREE;
2841 : }
2842 0 : }
2843 :
2844 : /* Second pass of diagnostic_manager::prune_path: remove redundant
2845 : interprocedural information.
2846 :
2847 : For example, given:
2848 : (1)- calling "f2" from "f1"
2849 : (2)--- entry to "f2"
2850 : (3)--- calling "f3" from "f2"
2851 : (4)----- entry to "f3"
2852 : (5)--- returning to "f2" to "f3"
2853 : (6)- returning to "f1" to "f2"
2854 : with no other intervening events, then none of these events are
2855 : likely to be interesting to the user.
2856 :
2857 : Prune [..., call, function-entry, return, ...] triples repeatedly
2858 : until nothing has changed. For the example above, this would
2859 : remove events (3, 4, 5), and then remove events (1, 2, 6). */
2860 :
2861 : void
2862 4161 : diagnostic_manager::prune_interproc_events (checker_path *path) const
2863 : {
2864 4161 : bool changed = false;
2865 4385 : do
2866 : {
2867 4385 : changed = false;
2868 4385 : int idx = (signed)path->num_events () - 1;
2869 22776 : while (idx >= 0)
2870 : {
2871 : /* Prune [..., call, function-entry, return, ...] triples. */
2872 18391 : if (idx + 2 < (signed)path->num_events ()
2873 10078 : && path->get_checker_event (idx)->is_call_p ()
2874 975 : && path->get_checker_event (idx + 1)->is_function_entry_p ()
2875 19358 : && path->get_checker_event (idx + 2)->is_return_p ())
2876 : {
2877 317 : if (get_logger ())
2878 : {
2879 0 : label_text desc
2880 0 : (path->get_checker_event (idx)->get_desc
2881 0 : (*global_dc->get_reference_printer ()));
2882 0 : log ("filtering events %i-%i:"
2883 : " irrelevant call/entry/return: %s",
2884 : idx, idx + 2, desc.get ());
2885 0 : }
2886 317 : path->delete_event (idx + 2);
2887 317 : path->delete_event (idx + 1);
2888 317 : path->delete_event (idx);
2889 317 : changed = true;
2890 317 : idx--;
2891 317 : continue;
2892 317 : }
2893 :
2894 : /* Prune [..., call, return, ...] pairs
2895 : (for -fanalyzer-verbosity=0). */
2896 18074 : if (idx + 1 < (signed)path->num_events ()
2897 13643 : && path->get_checker_event (idx)->is_call_p ()
2898 19103 : && path->get_checker_event (idx + 1)->is_return_p ())
2899 : {
2900 4 : if (get_logger ())
2901 : {
2902 0 : label_text desc
2903 0 : (path->get_checker_event (idx)->get_desc
2904 0 : (*global_dc->get_reference_printer ()));
2905 0 : log ("filtering events %i-%i:"
2906 : " irrelevant call/return: %s",
2907 : idx, idx + 1, desc.get ());
2908 0 : }
2909 4 : path->delete_event (idx + 1);
2910 4 : path->delete_event (idx);
2911 4 : changed = true;
2912 4 : idx--;
2913 4 : continue;
2914 4 : }
2915 :
2916 18070 : idx--;
2917 : }
2918 :
2919 : }
2920 : while (changed);
2921 4161 : }
2922 :
2923 : /* Remove everything within [call point, IDX]. For consistency,
2924 : IDX should represent the return event of the frame to delete,
2925 : or if there is none it should be the last event of the frame.
2926 : After this function, IDX designates the event prior to calling
2927 : this frame. */
2928 :
2929 : static void
2930 0 : prune_frame (checker_path *path, int &idx)
2931 : {
2932 0 : gcc_assert (idx >= 0);
2933 0 : int nesting = 1;
2934 0 : if (path->get_checker_event (idx)->is_return_p ())
2935 0 : nesting = 0;
2936 0 : do
2937 : {
2938 0 : if (path->get_checker_event (idx)->is_call_p ())
2939 0 : nesting--;
2940 0 : else if (path->get_checker_event (idx)->is_return_p ())
2941 0 : nesting++;
2942 :
2943 0 : path->delete_event (idx--);
2944 0 : } while (idx >= 0 && nesting != 0);
2945 0 : }
2946 :
2947 : /* This function is called when fanalyzer-show-events-in-system-headers
2948 : is disabled and will prune the diagnostic of all events within a
2949 : system header, only keeping the entry and exit events to the header.
2950 : This should be called after diagnostic_manager::prune_interproc_events
2951 : so that sucessive events [system header call, system header return]
2952 : are preserved thereafter.
2953 :
2954 : Given a diagnostics path diving into a system header in the form
2955 : [
2956 : prefix events...,
2957 : system header call,
2958 : system header entry,
2959 : events within system headers...,
2960 : system header return,
2961 : suffix events...
2962 : ]
2963 :
2964 : then transforms it into
2965 : [
2966 : prefix events...,
2967 : system header call,
2968 : system header return,
2969 : suffix events...
2970 : ]. */
2971 :
2972 : void
2973 4159 : diagnostic_manager::prune_system_headers (checker_path *path) const
2974 : {
2975 4159 : int idx = (signed)path->num_events () - 1;
2976 20197 : while (idx >= 0)
2977 : {
2978 16038 : const checker_event *event = path->get_checker_event (idx);
2979 : /* Prune everything between
2980 : [..., system entry, (...), system return, ...]. */
2981 16038 : if (event->is_return_p ()
2982 16038 : && in_system_header_at (event->get_location ()))
2983 : {
2984 0 : int ret_idx = idx;
2985 0 : prune_frame (path, idx);
2986 :
2987 0 : if (get_logger ())
2988 : {
2989 0 : log ("filtering system headers events %i-%i:",
2990 : idx, ret_idx);
2991 : }
2992 : // Delete function entry within system headers.
2993 0 : if (idx >= 0)
2994 : {
2995 0 : event = path->get_checker_event (idx);
2996 0 : if (event->is_function_entry_p ()
2997 0 : && in_system_header_at (event->get_location ()))
2998 : {
2999 0 : if (get_logger ())
3000 : {
3001 0 : label_text desc
3002 0 : (event->get_desc (*global_dc->get_reference_printer ()));
3003 0 : log ("filtering event %i:"
3004 : "system header entry event: %s",
3005 : idx, desc.get ());
3006 0 : }
3007 :
3008 0 : path->delete_event (idx);
3009 : }
3010 : }
3011 : }
3012 :
3013 16038 : idx--;
3014 : }
3015 4159 : }
3016 :
3017 : /* Return true iff event IDX within PATH is on the same line as REF_EXP_LOC. */
3018 :
3019 : static bool
3020 2804 : same_line_as_p (const expanded_location &ref_exp_loc,
3021 : checker_path *path, unsigned idx)
3022 : {
3023 2804 : const checker_event *ev = path->get_checker_event (idx);
3024 2804 : expanded_location idx_exp_loc = expand_location (ev->get_location ());
3025 2804 : gcc_assert (ref_exp_loc.file);
3026 2804 : if (idx_exp_loc.file == nullptr)
3027 : return false;
3028 2781 : if (strcmp (ref_exp_loc.file, idx_exp_loc.file))
3029 : return false;
3030 2781 : return ref_exp_loc.line == idx_exp_loc.line;
3031 : }
3032 :
3033 : /* This path-readability optimization reduces the verbosity of compound
3034 : conditional statements (without needing to reconstruct the AST, which
3035 : has already been lost).
3036 :
3037 : For example, it converts:
3038 :
3039 : | 61 | if (cp[0] != '\0' && cp[0] != '#')
3040 : | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3041 : | | | | |
3042 : | | | | (6) ...to here
3043 : | | | (7) following ‘true’ branch...
3044 : | | (5) following ‘true’ branch...
3045 : | 62 | {
3046 : | 63 | alias = cp++;
3047 : | | ~~~~
3048 : | | |
3049 : | | (8) ...to here
3050 :
3051 : into:
3052 :
3053 : | 61 | if (cp[0] != '\0' && cp[0] != '#')
3054 : | | ~
3055 : | | |
3056 : | | (5) following ‘true’ branch...
3057 : | 62 | {
3058 : | 63 | alias = cp++;
3059 : | | ~~~~
3060 : | | |
3061 : | | (6) ...to here
3062 :
3063 : by combining events 5-8 into new events 5-6.
3064 :
3065 : Find runs of consecutive (start_cfg_edge_event, end_cfg_edge_event) pairs
3066 : in which all events apart from the final end_cfg_edge_event are on the same
3067 : line, and for which either all the CFG edges are TRUE edges, or all are
3068 : FALSE edges.
3069 :
3070 : Consolidate each such run into a
3071 : (start_consolidated_cfg_edges_event, end_consolidated_cfg_edges_event)
3072 : pair. */
3073 :
3074 : void
3075 4161 : diagnostic_manager::consolidate_conditions (checker_path *path) const
3076 : {
3077 : /* Don't simplify edges if we're debugging them. */
3078 4161 : if (flag_analyzer_verbose_edges)
3079 : return;
3080 :
3081 11733 : for (int start_idx = 0;
3082 31697 : start_idx < (signed)path->num_events () - 1;
3083 : start_idx++)
3084 : {
3085 11733 : if (path->cfg_edge_pair_at_p (start_idx))
3086 : {
3087 2535 : const checker_event *old_start_ev
3088 2535 : = path->get_checker_event (start_idx);
3089 2535 : expanded_location start_exp_loc
3090 2535 : = expand_location (old_start_ev->get_location ());
3091 2535 : if (start_exp_loc.file == nullptr)
3092 2167 : continue;
3093 2535 : if (!same_line_as_p (start_exp_loc, path, start_idx + 1))
3094 2167 : continue;
3095 :
3096 : /* Are we looking for a run of all TRUE edges, or all FALSE edges? */
3097 368 : gcc_assert (old_start_ev->get_kind () == event_kind::start_cfg_edge);
3098 368 : const start_cfg_edge_event *old_start_cfg_ev
3099 : = (const start_cfg_edge_event *)old_start_ev;
3100 368 : bool edge_sense;
3101 368 : if (!old_start_cfg_ev->maybe_get_edge_sense (&edge_sense))
3102 0 : continue;
3103 :
3104 : /* Find a run of CFG start/end event pairs from
3105 : [start_idx, next_idx)
3106 : where all apart from the final event are on the same line,
3107 : and all are either TRUE or FALSE edges, matching the initial. */
3108 368 : int next_idx = start_idx + 2;
3109 368 : while (path->cfg_edge_pair_at_p (next_idx)
3110 487 : && same_line_as_p (start_exp_loc, path, next_idx))
3111 : {
3112 165 : const checker_event *iter_ev
3113 165 : = path->get_checker_event (next_idx);
3114 165 : gcc_assert (iter_ev->get_kind () == event_kind::start_cfg_edge);
3115 165 : const start_cfg_edge_event *iter_cfg_ev
3116 : = (const start_cfg_edge_event *)iter_ev;
3117 165 : bool iter_edge_sense;
3118 165 : if (!iter_cfg_ev->maybe_get_edge_sense (&iter_edge_sense))
3119 : break;
3120 162 : if (iter_edge_sense != edge_sense)
3121 : break;
3122 119 : next_idx += 2;
3123 : }
3124 :
3125 : /* If we have more than one pair in the run, consolidate. */
3126 368 : if (next_idx > start_idx + 2)
3127 : {
3128 111 : const checker_event *old_end_ev
3129 111 : = path->get_checker_event (next_idx - 1);
3130 111 : log ("consolidating CFG edge events %i-%i into %i-%i",
3131 : start_idx, next_idx - 1, start_idx, start_idx +1);
3132 111 : auto new_start_ev
3133 : = std::make_unique<start_consolidated_cfg_edges_event>
3134 111 : (event_loc_info (old_start_ev->get_location (),
3135 : old_start_ev->get_fndecl (),
3136 111 : old_start_ev->get_stack_depth ()),
3137 111 : edge_sense);
3138 111 : auto new_end_ev
3139 : = std::make_unique<end_consolidated_cfg_edges_event>
3140 111 : (event_loc_info (old_end_ev->get_location (),
3141 : old_end_ev->get_fndecl (),
3142 111 : old_end_ev->get_stack_depth ()));
3143 111 : path->replace_event (start_idx, std::move (new_start_ev));
3144 111 : path->replace_event (start_idx + 1, std::move (new_end_ev));
3145 111 : path->delete_events (start_idx + 2, next_idx - (start_idx + 2));
3146 111 : }
3147 : }
3148 : }
3149 : }
3150 :
3151 : /* Consolidate runs of consecutive unwind_event. */
3152 :
3153 : void
3154 4161 : diagnostic_manager::consolidate_unwind_events (checker_path *path) const
3155 : {
3156 : /* Don't simplify edges if we're debugging them. */
3157 4161 : if (flag_analyzer_verbose_edges)
3158 : return;
3159 :
3160 11727 : for (int start_idx = 0;
3161 31685 : start_idx < (signed)path->num_events () - 1;
3162 : start_idx++)
3163 : {
3164 : /* Find a run of consecutive unwind_event instances. */
3165 11727 : if (path->get_checker_event (start_idx)->get_kind ()
3166 : != event_kind::unwind)
3167 11718 : continue;
3168 9 : int iter_idx = start_idx + 1;
3169 15 : while (iter_idx < (int)path->num_events ())
3170 15 : if (path->get_checker_event (iter_idx)->get_kind ()
3171 : == event_kind::unwind)
3172 6 : ++iter_idx;
3173 : else
3174 : break;
3175 :
3176 : /* iter_idx should now be one after the last unwind_event in the run. */
3177 9 : const int last_idx = iter_idx - 1;
3178 9 : if (last_idx == start_idx)
3179 3 : continue;
3180 :
3181 6 : gcc_assert (last_idx > start_idx);
3182 :
3183 6 : log ("consolidating unwind events %i-%i into %i",
3184 : start_idx, last_idx, start_idx);
3185 :
3186 6 : unwind_event *first_event
3187 6 : = (unwind_event *)path->get_checker_event (start_idx);
3188 6 : const unwind_event *last_event
3189 6 : = (const unwind_event *)path->get_checker_event (last_idx);
3190 6 : first_event->m_num_frames += last_event->m_num_frames;
3191 6 : path->delete_events (start_idx + 1, last_idx - start_idx);
3192 : }
3193 : }
3194 :
3195 : /* Final pass of diagnostic_manager::prune_path.
3196 :
3197 : If all we're left with is in one function, then filter function entry
3198 : events. */
3199 :
3200 : void
3201 4161 : diagnostic_manager::finish_pruning (checker_path *path) const
3202 : {
3203 4161 : if (!path->interprocedural_p ())
3204 : {
3205 3353 : int idx = path->num_events () - 1;
3206 24377 : while (idx >= 0 && idx < (signed)path->num_events ())
3207 : {
3208 10512 : checker_event *base_event = path->get_checker_event (idx);
3209 10512 : if (base_event->get_kind () == event_kind::function_entry)
3210 : {
3211 3257 : log ("filtering event %i:"
3212 : " function entry for purely intraprocedural path", idx);
3213 3257 : path->delete_event (idx);
3214 : }
3215 10512 : idx--;
3216 : }
3217 : }
3218 4161 : }
3219 :
3220 : } // namespace ana
3221 :
3222 : #endif /* #if ENABLE_ANALYZER */
|