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