Line data Source code
1 : /* The analysis "engine".
2 : Copyright (C) 2019-2026 Free Software Foundation, Inc.
3 : Contributed by David Malcolm <dmalcolm@redhat.com>.
4 :
5 : This file is part of GCC.
6 :
7 : GCC is free software; you can redistribute it and/or modify it
8 : under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3, or (at your option)
10 : any later version.
11 :
12 : GCC is distributed in the hope that it will be useful, but
13 : WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with GCC; see the file COPYING3. If not see
19 : <http://www.gnu.org/licenses/>. */
20 :
21 : #include "analyzer/common.h"
22 :
23 : #include <zlib.h>
24 :
25 : #include "cfg.h"
26 : #include "gcc-rich-location.h"
27 : #include "gimple-iterator.h"
28 : #include "gimple-pretty-print.h"
29 : #include "cgraph.h"
30 : #include "fold-const.h"
31 : #include "digraph.h"
32 : #include "plugin.h"
33 : #include "target.h"
34 : #include "stringpool.h"
35 : #include "attribs.h"
36 : #include "tree-dfa.h"
37 : #include "gimple-predict.h"
38 : #include "context.h"
39 : #include "channels.h"
40 : #include "pretty-print-markup.h"
41 :
42 : #include "text-art/dump.h"
43 :
44 : #include "analyzer/analyzer-logging.h"
45 : #include "analyzer/call-string.h"
46 : #include "analyzer/program-point.h"
47 : #include "analyzer/store.h"
48 : #include "analyzer/region-model.h"
49 : #include "analyzer/constraint-manager.h"
50 : #include "analyzer/sm.h"
51 : #include "analyzer/pending-diagnostic.h"
52 : #include "analyzer/diagnostic-manager.h"
53 : #include "analyzer/supergraph.h"
54 : #include "analyzer/program-state.h"
55 : #include "analyzer/exploded-graph.h"
56 : #include "analyzer/exploded-path.h"
57 : #include "analyzer/analysis-plan.h"
58 : #include "analyzer/checker-path.h"
59 : #include "analyzer/state-purge.h"
60 : #include "analyzer/bar-chart.h"
61 : #include "analyzer/call-info.h"
62 : #include "analyzer/known-function-manager.h"
63 : #include "analyzer/call-summary.h"
64 : #include "analyzer/impl-sm-context.h"
65 :
66 : /* For an overview, see gcc/doc/analyzer.texi. */
67 :
68 : #if ENABLE_ANALYZER
69 :
70 : namespace ana {
71 :
72 : /* class impl_region_model_context : public region_model_context. */
73 :
74 1356340 : impl_region_model_context::
75 : impl_region_model_context (exploded_graph &eg,
76 : exploded_node *enode_for_diag,
77 : const program_state *old_state,
78 : program_state *new_state,
79 : uncertainty_t *uncertainty,
80 : path_context *path_ctxt,
81 : const gimple *stmt,
82 1356340 : bool *out_could_have_done_work)
83 1356340 : : m_eg (&eg), m_logger (eg.get_logger ()),
84 1356340 : m_enode_for_diag (enode_for_diag),
85 1356340 : m_old_state (old_state),
86 1356340 : m_new_state (new_state),
87 1356340 : m_stmt (stmt),
88 1356340 : m_ext_state (eg.get_ext_state ()),
89 1356340 : m_uncertainty (uncertainty),
90 1356340 : m_path_ctxt (path_ctxt),
91 1356340 : m_out_could_have_done_work (out_could_have_done_work)
92 : {
93 1356340 : }
94 :
95 4 : impl_region_model_context::
96 : impl_region_model_context (program_state *state,
97 : const extrinsic_state &ext_state,
98 : uncertainty_t *uncertainty,
99 4 : logger *logger)
100 4 : : m_eg (nullptr), m_logger (logger), m_enode_for_diag (nullptr),
101 4 : m_old_state (nullptr),
102 4 : m_new_state (state),
103 4 : m_stmt (nullptr),
104 4 : m_ext_state (ext_state),
105 4 : m_uncertainty (uncertainty),
106 4 : m_path_ctxt (nullptr),
107 4 : m_out_could_have_done_work (nullptr)
108 : {
109 4 : }
110 :
111 : bool
112 3199 : impl_region_model_context::warn_at (std::unique_ptr<pending_diagnostic> d,
113 : pending_location &&ploc)
114 : {
115 3199 : LOG_FUNC (get_logger ());
116 3199 : if (m_eg)
117 : {
118 3199 : bool terminate_path = d->terminate_path_p ();
119 3199 : if (m_eg->get_diagnostic_manager ().add_diagnostic (std::move (ploc),
120 : std::move (d)))
121 : {
122 3160 : if (m_path_ctxt
123 2252 : && terminate_path
124 867 : && flag_analyzer_suppress_followups)
125 662 : m_path_ctxt->terminate_path ();
126 3160 : return true;
127 : }
128 : }
129 : return false;
130 3199 : }
131 :
132 : void
133 199 : impl_region_model_context::add_note (std::unique_ptr<pending_note> pn)
134 : {
135 199 : LOG_FUNC (get_logger ());
136 199 : if (m_eg)
137 199 : m_eg->get_diagnostic_manager ().add_note (std::move (pn));
138 199 : }
139 :
140 : void
141 164 : impl_region_model_context::add_event (std::unique_ptr<checker_event> event)
142 : {
143 164 : LOG_FUNC (get_logger ());
144 164 : if (m_eg)
145 164 : m_eg->get_diagnostic_manager ().add_event (std::move (event));
146 164 : }
147 :
148 : void
149 86764 : impl_region_model_context::on_svalue_leak (const svalue *sval)
150 :
151 : {
152 867303 : for (sm_state_map *smap : m_new_state->m_checker_states)
153 607011 : smap->on_svalue_leak (sval, this);
154 86764 : }
155 :
156 : void
157 485939 : impl_region_model_context::
158 : on_liveness_change (const svalue_set &live_svalues,
159 : const region_model *model)
160 : {
161 4857229 : for (sm_state_map *smap : m_new_state->m_checker_states)
162 3399412 : smap->on_liveness_change (live_svalues, model, m_ext_state, this);
163 485939 : }
164 :
165 : void
166 51974 : impl_region_model_context::on_unknown_change (const svalue *sval,
167 : bool is_mutable)
168 : {
169 51974 : if (!sval->can_have_associated_state_p ())
170 : return;
171 422585 : for (sm_state_map *smap : m_new_state->m_checker_states)
172 295784 : smap->on_unknown_change (sval, is_mutable, m_ext_state);
173 : }
174 :
175 : void
176 298 : impl_region_model_context::on_escaped_function (tree fndecl)
177 : {
178 298 : m_eg->on_escaped_function (fndecl);
179 298 : }
180 :
181 : uncertainty_t *
182 4122994 : impl_region_model_context::get_uncertainty ()
183 : {
184 4122994 : return m_uncertainty;
185 : }
186 :
187 : /* Purge state involving SVAL. The region_model has already been purged,
188 : so we only need to purge other state in the program_state:
189 : the sm-state. */
190 :
191 : void
192 15664 : impl_region_model_context::purge_state_involving (const svalue *sval)
193 : {
194 15664 : int i;
195 15664 : sm_state_map *smap;
196 125312 : FOR_EACH_VEC_ELT (m_new_state->m_checker_states, i, smap)
197 109648 : smap->purge_state_involving (sval, m_ext_state);
198 15664 : }
199 :
200 : void
201 8489 : impl_region_model_context::bifurcate (std::unique_ptr<custom_edge_info> info)
202 : {
203 8489 : if (m_path_ctxt)
204 8489 : m_path_ctxt->bifurcate (std::move (info));
205 8489 : }
206 :
207 : void
208 1360 : impl_region_model_context::terminate_path ()
209 : {
210 1360 : if (m_path_ctxt)
211 1323 : return m_path_ctxt->terminate_path ();
212 : }
213 :
214 : bool
215 727148 : impl_region_model_context::
216 : get_state_map_by_name (const char *name,
217 : sm_state_map **out_smap,
218 : const state_machine **out_sm,
219 : unsigned *out_sm_idx,
220 : std::unique_ptr<sm_context> *out_sm_context)
221 : {
222 727148 : if (!m_new_state)
223 : return false;
224 :
225 724137 : unsigned sm_idx;
226 724137 : if (!m_ext_state.get_sm_idx_by_name (name, &sm_idx))
227 : return false;
228 :
229 723670 : const state_machine *sm = &m_ext_state.get_sm (sm_idx);
230 723670 : sm_state_map *new_smap = m_new_state->m_checker_states[sm_idx];
231 :
232 723670 : *out_smap = new_smap;
233 723670 : *out_sm = sm;
234 723670 : if (out_sm_idx)
235 722852 : *out_sm_idx = sm_idx;
236 723670 : if (out_sm_context)
237 : {
238 748 : const sm_state_map *old_smap = m_old_state->m_checker_states[sm_idx];
239 748 : *out_sm_context
240 748 : = std::make_unique<impl_sm_context> (*m_eg,
241 : sm_idx,
242 : *sm,
243 748 : m_enode_for_diag,
244 748 : m_old_state,
245 748 : m_new_state,
246 : old_smap,
247 : new_smap,
248 748 : m_path_ctxt,
249 1496 : false);
250 : }
251 : return true;
252 : }
253 :
254 : /* Subclass of pending_location::fixer_for_epath for finding the best stmt
255 : to report the leak at, given the emission path. */
256 :
257 : class leak_ploc_fixer_for_epath : public pending_location::fixer_for_epath
258 : {
259 : public:
260 1312 : leak_ploc_fixer_for_epath (const exploded_graph &eg, tree var)
261 1312 : : m_eg (eg), m_var (var)
262 : {}
263 :
264 : void
265 1196 : fixup_for_epath (const exploded_path &epath,
266 : pending_location &ploc) const final override
267 : {
268 1196 : logger * const logger = m_eg.get_logger ();
269 1196 : LOG_FUNC (logger);
270 :
271 : /* Handle the interprocedural case where we leak the retval at a return
272 : because the caller discards the return value. */
273 1196 : if (m_var
274 976 : && TREE_CODE (m_var) == RESULT_DECL)
275 : {
276 6 : auto &point = ploc.m_enode->get_point ();
277 12 : if (point.get_stack_depth () > 1)
278 6 : if (point.get_supernode ()->exit_p ())
279 : {
280 : /* Get the program_point for the call within the caller. */
281 6 : auto &cs = point.get_call_string ();
282 6 : auto caller_snode = cs.get_return_node_in_caller ();
283 6 : gcc_assert (caller_snode);
284 6 : program_point caller_point (caller_snode, *cs.get_parent ());
285 6 : ploc.m_event_loc_info = event_loc_info (caller_point);
286 6 : return;
287 : }
288 : }
289 :
290 : /* Handle the case where we have e.g.:
291 : | var = malloc (N);
292 : | var = NULL;
293 : which with SSA becomes e.g.:
294 : | var_0 = malloc (N);
295 : | var_1 = nullptr;
296 : and thus leads to the leak being found at the enode where "var_0" goes
297 : out of scope.
298 : Fix up the location of the leak to report it at the write of NULL. */
299 1190 : if (m_var && TREE_CODE (m_var) == SSA_NAME)
300 : {
301 778 : log_scope sentinel (logger, "looking for write to sibling SSA name");
302 : /* Locate the final write to this SSA name in the path. */
303 778 : const gimple *def_stmt = SSA_NAME_DEF_STMT (m_var);
304 :
305 778 : int idx_of_def_stmt;
306 778 : if (epath.find_stmt_backwards (def_stmt, &idx_of_def_stmt))
307 : {
308 : /* What was the next write to the underlying var
309 : after the SSA name was set? (if any). */
310 :
311 670 : for (unsigned idx = idx_of_def_stmt + 1;
312 7758 : idx < epath.m_elements.size ();
313 : ++idx)
314 : {
315 7090 : const exploded_edge *eedge = epath.m_elements[idx].m_eedge;
316 7090 : if (logger)
317 0 : logger->log ("eedge[%i]: EN %i -> EN %i",
318 : idx,
319 0 : eedge->m_src->m_index,
320 0 : eedge->m_dest->m_index);
321 7090 : const gimple *stmt = eedge->maybe_get_stmt ();
322 7090 : if (!stmt)
323 2190 : continue;
324 8950 : if (const gassign *assign = dyn_cast <const gassign *> (stmt))
325 : {
326 1862 : tree lhs = gimple_assign_lhs (assign);
327 1862 : if (TREE_CODE (lhs) == SSA_NAME
328 3098 : && SSA_NAME_VAR (lhs) == SSA_NAME_VAR (m_var))
329 : {
330 2 : if (logger)
331 0 : logger->log ("using location 0%lx from gassign",
332 0 : assign->location);
333 2 : ploc.m_event_loc_info.m_loc = assign->location;
334 2 : return;
335 : }
336 : }
337 : }
338 : }
339 778 : }
340 :
341 : /* If the epath ends at a function exit node, the location is at
342 : the final "}". Try walking backward along EPATH, looking for a
343 : the first suitable stmt with a better location. */
344 1188 : gcc_assert (ploc.m_enode->get_supernode ());
345 1188 : const greturn *return_stmt = nullptr;
346 1188 : if (ploc.m_enode->get_supernode ()->exit_p ()
347 1188 : && has_return_stmt_p (epath, return_stmt, logger))
348 : {
349 : /* If we have "return SSA_NAME;" on EPATH, keep track of the
350 : pertinent SSA name as we walk backwards through EPATH. */
351 730 : tree retval = NULL_TREE;
352 730 : if (return_stmt)
353 730 : retval = gimple_return_retval (return_stmt);
354 :
355 730 : log_scope sentinel (logger, "walking backward along epath");
356 3034 : for (int idx = epath.m_elements.size () - 1; idx >= 0; --idx)
357 : {
358 2962 : const exploded_path::element_t &element = epath.m_elements[idx];
359 2962 : const exploded_edge *eedge = element.m_eedge;
360 2962 : if (logger)
361 : {
362 0 : logger->log ("eedge[%i]: EN %i -> EN %i",
363 : idx,
364 0 : eedge->m_src->m_index,
365 0 : eedge->m_dest->m_index);
366 0 : if (retval)
367 0 : logger->log (" retval: %qE", retval);
368 : }
369 2962 : if (auto op = eedge->maybe_get_op ())
370 : {
371 1914 : if (retval)
372 406 : if (auto phis = op->dyn_cast_phis_for_edge_op ())
373 : {
374 184 : for (auto iter : phis->get_pairs ())
375 92 : if (retval == iter.m_dst)
376 : {
377 : /* We have "PHI(RETVAL = SRC);"
378 : Track SRC instead */
379 92 : retval = iter.m_src;
380 92 : if (logger)
381 0 : logger->log ("updating retval to %qE", retval);
382 : }
383 : }
384 1914 : if (const gimple *stmt = op->maybe_get_stmt ())
385 1808 : if (consider_stmt_location_p (*stmt, retval))
386 870 : if (useful_location_p (stmt->location))
387 : {
388 658 : if (logger)
389 0 : logger->log ("using location 0x%lx from stmt",
390 0 : stmt->location);
391 658 : ploc.m_event_loc_info.m_loc = stmt->location;
392 658 : return;
393 : }
394 : }
395 : }
396 730 : }
397 1196 : }
398 :
399 : private:
400 : static bool
401 730 : has_return_stmt_p (const exploded_path &epath,
402 : const greturn *&out_greturn,
403 : logger *logger)
404 : {
405 730 : LOG_SCOPE (logger);
406 :
407 1254 : for (int idx = epath.m_elements.size () - 1; idx >= 0; --idx)
408 : {
409 1254 : const exploded_path::element_t &element = epath.m_elements[idx];
410 1254 : const exploded_edge *eedge = element.m_eedge;
411 1254 : if (eedge->m_src->get_stack_depth ()
412 2508 : != eedge->m_dest->get_stack_depth ())
413 : {
414 : /* We have interprocedural activity, and
415 : presumably are no longer in the function where
416 : EPATH terminates.
417 : Give up. */
418 : return false;
419 : }
420 1254 : if (auto op = eedge->maybe_get_op ())
421 : {
422 730 : switch (op->get_kind ())
423 : {
424 : default:
425 : break;
426 730 : case operation::kind::return_stmt:
427 730 : if (logger)
428 0 : logger->log ("found return_stmt");
429 730 : out_greturn = &((const greturn_op *)op)->get_greturn ();
430 730 : return true;
431 0 : case operation::kind::predict_stmt:
432 0 : {
433 0 : auto &stmt = ((const gimple_stmt_op *)op)->get_stmt ();
434 0 : switch (gimple_predict_predictor (&stmt))
435 : {
436 0 : case PRED_TREE_EARLY_RETURN:
437 : /* Assume this is due to a "return;" in the user's
438 : code. */
439 0 : if (logger)
440 0 : logger->log ("assuming a return: PRED_TREE_EARLY_RETURN");
441 0 : return true;
442 : default:
443 : break;
444 : }
445 : }
446 : break;
447 : }
448 : }
449 : }
450 : return false;
451 730 : }
452 :
453 : /* When certain statements show up on the epath of a leak
454 : at an exit node, if they have locations, these locations
455 : tend to be better locations for the leak.
456 : Return true for such statements (but without checking their
457 : locations). */
458 : static bool
459 1808 : consider_stmt_location_p (const gimple &stmt,
460 : tree retval)
461 : {
462 1808 : if (retval && TREE_CODE (retval) == SSA_NAME)
463 312 : if (&stmt == SSA_NAME_DEF_STMT (retval))
464 : return true;
465 :
466 1784 : switch (stmt.code)
467 : {
468 : default:
469 : break;
470 526 : case GIMPLE_CALL:
471 526 : {
472 526 : const gcall &call = *as_a <const gcall *> (&stmt);
473 526 : if (is_cxa_end_catch_p (call))
474 : return true;
475 : }
476 : break;
477 : case GIMPLE_PREDICT:
478 : case GIMPLE_RETURN:
479 : return true;
480 : }
481 : return false;
482 : }
483 :
484 : const exploded_graph &m_eg;
485 : tree m_var;
486 : };
487 :
488 : std::unique_ptr<pending_location::fixer_for_epath>
489 0 : make_ploc_fixer_for_epath_for_leak_diagnostic (const exploded_graph &eg,
490 : tree var)
491 : {
492 0 : return std::make_unique<leak_ploc_fixer_for_epath> (eg, var);
493 : }
494 :
495 : /* A measurement of how good EXPR is for presenting to the user, so
496 : that e.g. we can say prefer printing
497 : "leak of 'tmp.m_ptr'"
498 : over:
499 : "leak of '<unknown>'". */
500 :
501 : static int
502 10792 : readability (const_tree expr)
503 : {
504 : /* Arbitrarily-chosen "high readability" value. */
505 19983 : const int HIGH_READABILITY = 65536;
506 :
507 19983 : gcc_assert (expr);
508 19983 : switch (TREE_CODE (expr))
509 : {
510 2526 : case COMPONENT_REF:
511 2526 : case MEM_REF:
512 : /* Impose a slight readability penalty relative to that of
513 : operand 0. */
514 2526 : return readability (TREE_OPERAND (expr, 0)) - 16;
515 :
516 7808 : case SSA_NAME:
517 7808 : {
518 7808 : if (tree var = SSA_NAME_VAR (expr))
519 : {
520 5758 : if (DECL_ARTIFICIAL (var))
521 : {
522 : /* If we have an SSA name for an artificial var,
523 : only use it if it has a debug expr associated with
524 : it that fixup_tree_for_diagnostic can use. */
525 132 : if (VAR_P (var) && DECL_HAS_DEBUG_EXPR_P (var))
526 0 : return readability (DECL_DEBUG_EXPR (var)) - 1;
527 : }
528 : else
529 : {
530 : /* Slightly favor the underlying var over the SSA name to
531 : avoid having them compare equal. */
532 5626 : return readability (var) - 1;
533 : }
534 : }
535 : /* Avoid printing '<unknown>' for SSA names for temporaries. */
536 : return -1;
537 : }
538 6982 : break;
539 :
540 6982 : case PARM_DECL:
541 6982 : case VAR_DECL:
542 6982 : if (DECL_NAME (expr))
543 : return HIGH_READABILITY;
544 : else
545 : /* We don't want to print temporaries. For example, the C FE
546 : prints them as e.g. "<Uxxxx>" where "xxxx" is the low 16 bits
547 : of the tree pointer (see pp_c_tree_decl_identifier). */
548 : return -1;
549 :
550 : case RESULT_DECL:
551 : /* Printing "<return-value>" isn't ideal, but is less awful than
552 : trying to print a temporary. */
553 : return HIGH_READABILITY / 2;
554 :
555 1039 : case NOP_EXPR:
556 1039 : {
557 : /* Impose a moderate readability penalty for casts. */
558 1039 : const int CAST_PENALTY = 32;
559 1039 : return readability (TREE_OPERAND (expr, 0)) - CAST_PENALTY;
560 : }
561 :
562 : case INTEGER_CST:
563 : return HIGH_READABILITY;
564 :
565 184 : default:
566 184 : return 0;
567 : }
568 :
569 : return 0;
570 : }
571 :
572 : /* A qsort comparator for trees to sort them into most user-readable to
573 : least user-readable. */
574 :
575 : int
576 5396 : readability_comparator (const void *p1, const void *p2)
577 : {
578 5396 : path_var pv1 = *(path_var const *)p1;
579 5396 : path_var pv2 = *(path_var const *)p2;
580 :
581 5396 : const int tree_r1 = readability (pv1.m_tree);
582 5396 : const int tree_r2 = readability (pv2.m_tree);
583 :
584 : /* Favor items that are deeper on the stack and hence more recent;
585 : this also favors locals over globals. */
586 5396 : const int COST_PER_FRAME = 64;
587 5396 : const int depth_r1 = pv1.m_stack_depth * COST_PER_FRAME;
588 5396 : const int depth_r2 = pv2.m_stack_depth * COST_PER_FRAME;
589 :
590 : /* Combine the scores from the tree and from the stack depth.
591 : This e.g. lets us have a slightly penalized cast in the most
592 : recent stack frame "beat" an uncast value in an older stack frame. */
593 5396 : const int sum_r1 = tree_r1 + depth_r1;
594 5396 : const int sum_r2 = tree_r2 + depth_r2;
595 5396 : if (int cmp = sum_r2 - sum_r1)
596 : return cmp;
597 :
598 : /* Otherwise, more readable trees win. */
599 950 : if (int cmp = tree_r2 - tree_r1)
600 : return cmp;
601 :
602 : /* Otherwise, if they have the same readability, then impose an
603 : arbitrary deterministic ordering on them. */
604 :
605 950 : if (int cmp = TREE_CODE (pv1.m_tree) - TREE_CODE (pv2.m_tree))
606 : return cmp;
607 :
608 890 : switch (TREE_CODE (pv1.m_tree))
609 : {
610 : default:
611 : break;
612 860 : case SSA_NAME:
613 860 : if (int cmp = (SSA_NAME_VERSION (pv1.m_tree)
614 860 : - SSA_NAME_VERSION (pv2.m_tree)))
615 : return cmp;
616 : break;
617 0 : case PARM_DECL:
618 0 : case VAR_DECL:
619 0 : case RESULT_DECL:
620 0 : if (int cmp = DECL_UID (pv1.m_tree) - DECL_UID (pv2.m_tree))
621 : return cmp;
622 : break;
623 : }
624 :
625 : /* TODO: We ought to find ways of sorting such cases. */
626 : return 0;
627 : }
628 :
629 : /* Return true is SNODE is the EXIT node of a function, or is one
630 : of the final snodes within its function.
631 :
632 : Specifically, handle the final supernodes before the EXIT node,
633 : for the case of clobbers that happen immediately before exiting.
634 : We need a run of snodes leading to the return_p snode, where all edges are
635 : intraprocedural, and every snode has just one successor.
636 :
637 : We use this when suppressing leak reports at the end of "main". */
638 :
639 : static bool
640 1627 : returning_from_function_p (const supernode *snode)
641 : {
642 1627 : if (!snode)
643 : return false;
644 :
645 : unsigned count = 0;
646 : const supernode *iter = snode;
647 2628 : while (true)
648 : {
649 2628 : if (iter->exit_p ())
650 : return true;
651 1737 : if (iter->m_succs.length () != 1)
652 : return false;
653 1570 : const superedge *sedge = iter->m_succs[0];
654 :
655 1570 : if (auto op = sedge->get_op ())
656 1279 : if (op->get_kind () == operation::kind::return_stmt)
657 : return true;
658 :
659 1035 : iter = sedge->m_dest;
660 :
661 : /* Impose a limit to ensure we terminate for pathological cases.
662 :
663 : We only care about the final 3 nodes, due to cases like:
664 : BB:
665 : (clobber causing leak)
666 :
667 : BB:
668 : <label>:
669 : return _val;
670 :
671 : EXIT BB.*/
672 1035 : if (++count > 4)
673 : return false;
674 : }
675 : }
676 :
677 : /* Find the best tree for SVAL and call SM's on_leak vfunc with it.
678 : If on_leak returns a pending_diagnostic, queue it up to be reported,
679 : so that we potentially complain about a leak of SVAL in the given STATE. */
680 :
681 : void
682 1627 : impl_region_model_context::on_state_leak (const state_machine &sm,
683 : const svalue *sval,
684 : state_machine::state_t state)
685 : {
686 1627 : logger * const logger = get_logger ();
687 1627 : LOG_SCOPE (logger);
688 1627 : if (logger)
689 : {
690 0 : logger->start_log_line ();
691 0 : logger->log_partial ("considering leak of ");
692 0 : sval->dump_to_pp (logger->get_printer (), true);
693 0 : logger->end_log_line ();
694 : }
695 :
696 1627 : if (!m_eg)
697 : return;
698 :
699 : /* m_old_state also needs to be non-NULL so that the sm_ctxt can look
700 : up the old state of SVAL. */
701 1627 : gcc_assert (m_old_state);
702 :
703 : /* SVAL has leaked within the new state: it is not used by any reachable
704 : regions.
705 : We need to convert it back to a tree, but since it's likely no regions
706 : use it, we have to find the "best" tree for it in the old_state. */
707 1627 : svalue_set visited;
708 1627 : path_var leaked_pv
709 1627 : = m_old_state->m_region_model->get_representative_path_var (sval,
710 : &visited,
711 : nullptr);
712 :
713 : /* Strip off top-level casts */
714 1627 : if (leaked_pv.m_tree && TREE_CODE (leaked_pv.m_tree) == NOP_EXPR)
715 94 : leaked_pv.m_tree = TREE_OPERAND (leaked_pv.m_tree, 0);
716 :
717 : /* This might be NULL; the pending_diagnostic subclasses need to cope
718 : with this. */
719 1627 : tree leaked_tree = leaked_pv.m_tree;
720 1627 : if (logger)
721 : {
722 0 : if (leaked_tree)
723 0 : logger->log ("best leaked_tree: %qE", leaked_tree);
724 : else
725 0 : logger->log ("best leaked_tree: NULL");
726 : }
727 :
728 1627 : gcc_assert (m_enode_for_diag);
729 :
730 : /* Don't complain about leaks when returning from "main". */
731 1627 : if (returning_from_function_p (m_enode_for_diag->get_supernode ()))
732 : {
733 1460 : tree fndecl = m_enode_for_diag->get_function ()->decl;
734 1460 : if (id_equal (DECL_NAME (fndecl), "main"))
735 : {
736 66 : if (logger)
737 0 : logger->log ("not reporting leak from main");
738 66 : return;
739 : }
740 : }
741 :
742 1561 : tree leaked_tree_for_diag = fixup_tree_for_diagnostic (leaked_tree);
743 1561 : std::unique_ptr<pending_diagnostic> pd = sm.on_leak (leaked_tree_for_diag,
744 : m_old_state,
745 1561 : m_new_state);
746 1561 : if (pd)
747 : {
748 1312 : pending_location ploc (get_pending_location_for_diag ());
749 1312 : ploc.m_fixer_for_epath
750 1312 : = std::make_unique<leak_ploc_fixer_for_epath> (*m_eg, leaked_tree);
751 1312 : m_eg->get_diagnostic_manager ().add_diagnostic
752 1312 : (&sm, std::move (ploc),
753 : leaked_tree_for_diag, sval, state, std::move (pd));
754 1312 : }
755 1627 : }
756 :
757 : /* Implementation of region_model_context::on_condition vfunc.
758 : Notify all state machines about the condition, which could lead to
759 : state transitions. */
760 :
761 : void
762 34881 : impl_region_model_context::on_condition (const svalue *lhs,
763 : enum tree_code op,
764 : const svalue *rhs)
765 : {
766 34881 : int sm_idx;
767 34881 : sm_state_map *smap;
768 278838 : FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
769 : {
770 243957 : const state_machine &sm = m_ext_state.get_sm (sm_idx);
771 487914 : impl_sm_context sm_ctxt (*m_eg, sm_idx, sm, m_enode_for_diag,
772 : m_old_state, m_new_state,
773 487914 : m_old_state->m_checker_states[sm_idx],
774 487914 : m_new_state->m_checker_states[sm_idx],
775 243957 : m_path_ctxt);
776 243957 : sm.on_condition (sm_ctxt, lhs, op, rhs);
777 243957 : }
778 34881 : }
779 :
780 : /* Implementation of region_model_context::on_bounded_ranges vfunc.
781 : Notify all state machines about the ranges, which could lead to
782 : state transitions. */
783 :
784 : void
785 6099 : impl_region_model_context::on_bounded_ranges (const svalue &sval,
786 : const bounded_ranges &ranges)
787 : {
788 6099 : int sm_idx;
789 6099 : sm_state_map *smap;
790 48792 : FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
791 : {
792 42693 : const state_machine &sm = m_ext_state.get_sm (sm_idx);
793 85386 : impl_sm_context sm_ctxt (*m_eg, sm_idx, sm, m_enode_for_diag,
794 : m_old_state, m_new_state,
795 85386 : m_old_state->m_checker_states[sm_idx],
796 85386 : m_new_state->m_checker_states[sm_idx],
797 42693 : m_path_ctxt);
798 42693 : sm.on_bounded_ranges (sm_ctxt, sval, ranges);
799 42693 : }
800 6099 : }
801 :
802 : /* Implementation of region_model_context::on_pop_frame vfunc.
803 : Notify all state machines about the frame being popped, which
804 : could lead to states being discarded. */
805 :
806 : void
807 23725 : impl_region_model_context::on_pop_frame (const frame_region *frame_reg)
808 : {
809 23725 : int sm_idx;
810 23725 : sm_state_map *smap;
811 189432 : FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
812 : {
813 165707 : const state_machine &sm = m_ext_state.get_sm (sm_idx);
814 165707 : sm.on_pop_frame (smap, frame_reg);
815 : }
816 23725 : }
817 :
818 : /* Implementation of region_model_context::on_phi vfunc.
819 : Notify all state machines about the phi, which could lead to
820 : state transitions. */
821 :
822 : void
823 0 : impl_region_model_context::on_phi (const gphi *phi, tree rhs)
824 : {
825 0 : int sm_idx;
826 0 : sm_state_map *smap;
827 0 : FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
828 : {
829 0 : const state_machine &sm = m_ext_state.get_sm (sm_idx);
830 0 : impl_sm_context sm_ctxt (*m_eg, sm_idx, sm, m_enode_for_diag,
831 : m_old_state, m_new_state,
832 0 : m_old_state->m_checker_states[sm_idx],
833 0 : m_new_state->m_checker_states[sm_idx],
834 0 : m_path_ctxt);
835 0 : sm.on_phi (sm_ctxt, phi, rhs);
836 0 : }
837 0 : }
838 :
839 : /* Implementation of region_model_context::on_unexpected_tree_code vfunc.
840 : Mark the new state as being invalid for further exploration.
841 : TODO(stage1): introduce a warning for when this occurs. */
842 :
843 : void
844 52 : impl_region_model_context::on_unexpected_tree_code (tree t,
845 : const dump_location_t &loc)
846 : {
847 52 : logger * const logger = get_logger ();
848 52 : if (logger)
849 : {
850 2 : const dump_impl_location_t &impl_loc = loc.get_impl_location ();
851 2 : const char *unknown = "<unknown>";
852 2 : logger->log ("unhandled tree code: %qs in %qs at %s:%i",
853 2 : get_tree_code_name (TREE_CODE (t)),
854 2 : impl_loc.m_function ? impl_loc.m_function : unknown,
855 2 : impl_loc.m_file ? impl_loc.m_file : unknown,
856 2 : impl_loc.m_line);
857 : }
858 52 : if (m_new_state)
859 52 : m_new_state->m_valid = false;
860 52 : }
861 :
862 : /* Implementation of region_model_context::maybe_did_work vfunc.
863 : Mark that "externally visible work" has occurred, and thus we
864 : shouldn't report an infinite loop here. */
865 :
866 : void
867 24497 : impl_region_model_context::maybe_did_work ()
868 : {
869 24497 : if (m_out_could_have_done_work)
870 22962 : *m_out_could_have_done_work = true;
871 24497 : }
872 :
873 : pending_location
874 4511 : impl_region_model_context::get_pending_location_for_diag () const
875 : {
876 4511 : if (m_stmt && useful_location_p (m_stmt->location))
877 3183 : return pending_location (m_enode_for_diag, m_stmt->location);
878 : else
879 1328 : return pending_location (m_enode_for_diag);
880 : }
881 :
882 : /* struct point_and_state. */
883 :
884 : /* Assert that this object is sane. */
885 :
886 : void
887 792205 : point_and_state::validate (const extrinsic_state &ext_state) const
888 : {
889 : /* Skip this in a release build. */
890 : #if !CHECKING_P
891 : return;
892 : #endif
893 :
894 792205 : m_point.validate ();
895 :
896 792205 : m_state.validate (ext_state);
897 :
898 : /* Verify that the callstring's model of the stack corresponds to that
899 : of the region_model. */
900 : /* They should have the same depth. */
901 1577568 : gcc_assert (m_point.get_stack_depth ()
902 : == m_state.m_region_model->get_stack_depth ());
903 : /* Check the functions in the callstring vs those in the frames
904 : at each depth. */
905 1158199 : for (const frame_region *iter_frame
906 792205 : = m_state.m_region_model->get_current_frame ();
907 1950404 : iter_frame; iter_frame = iter_frame->get_calling_frame ())
908 : {
909 1158199 : int index = iter_frame->get_index ();
910 1158199 : gcc_assert (m_point.get_function_at_depth (index)
911 : == &iter_frame->get_function ());
912 : }
913 792205 : }
914 :
915 : /* Subroutine of print_enode_indices: print a run of indices from START_IDX
916 : to END_IDX to PP, using and updating *FIRST_RUN. */
917 :
918 : static void
919 9907 : print_run (pretty_printer *pp, int start_idx, int end_idx,
920 : bool *first_run)
921 : {
922 9907 : if (!(*first_run))
923 6992 : pp_string (pp, ", ");
924 9907 : *first_run = false;
925 9907 : if (start_idx == end_idx)
926 6935 : pp_printf (pp, "EN: %i", start_idx);
927 : else
928 2972 : pp_printf (pp, "EN: %i-%i", start_idx, end_idx);
929 9907 : }
930 :
931 : /* Print the indices within ENODES to PP, collecting them as
932 : runs/singletons e.g. "EN: 4-7, EN: 20-23, EN: 42". */
933 :
934 : static void
935 2927 : print_enode_indices (pretty_printer *pp,
936 : const auto_vec<exploded_node *> &enodes)
937 : {
938 2927 : int cur_start_idx = -1;
939 2927 : int cur_finish_idx = -1;
940 2927 : bool first_run = true;
941 2927 : unsigned i;
942 2927 : exploded_node *enode;
943 21544 : FOR_EACH_VEC_ELT (enodes, i, enode)
944 : {
945 18617 : if (cur_start_idx == -1)
946 : {
947 2915 : gcc_assert (cur_finish_idx == -1);
948 2915 : cur_start_idx = cur_finish_idx = enode->m_index;
949 : }
950 : else
951 : {
952 15702 : if (enode->m_index == cur_finish_idx + 1)
953 : /* Continuation of a run. */
954 : cur_finish_idx = enode->m_index;
955 : else
956 : {
957 : /* Finish existing run, start a new one. */
958 6992 : gcc_assert (cur_start_idx >= 0);
959 6992 : gcc_assert (cur_finish_idx >= 0);
960 6992 : print_run (pp, cur_start_idx, cur_finish_idx,
961 : &first_run);
962 6992 : cur_start_idx = cur_finish_idx = enode->m_index;
963 : }
964 : }
965 : }
966 : /* Finish any existing run. */
967 2927 : if (cur_start_idx >= 0)
968 : {
969 2915 : gcc_assert (cur_finish_idx >= 0);
970 2915 : print_run (pp, cur_start_idx, cur_finish_idx,
971 : &first_run);
972 : }
973 2927 : }
974 :
975 : /* struct eg_traits::dump_args_t. */
976 :
977 : /* The <FILENAME>.eg.dot output can quickly become unwieldy if we show
978 : full details for all enodes (both in terms of CPU time to render it,
979 : and in terms of being meaningful to a human viewing it).
980 :
981 : If we show just the IDs then the resulting graph is usually viewable,
982 : but then we have to keep switching back and forth between the .dot
983 : view and other dumps.
984 :
985 : This function implements a heuristic for showing detail at the enodes
986 : that (we hope) matter, and just the ID at other enodes, fixing the CPU
987 : usage of the .dot viewer, and drawing the attention of the viewer
988 : to these enodes.
989 :
990 : Return true if ENODE should be shown in detail in .dot output.
991 : Return false if no detail should be shown for ENODE. */
992 :
993 : bool
994 507 : eg_traits::dump_args_t::show_enode_details_p (const exploded_node &enode) const
995 : {
996 : /* If the number of exploded nodes isn't too large, we may as well show
997 : all enodes in full detail in the .dot output. */
998 507 : if (m_eg.m_nodes.length ()
999 507 : <= (unsigned) param_analyzer_max_enodes_for_full_dump)
1000 : return true;
1001 :
1002 : /* Otherwise, assume that what's most interesting are state explosions,
1003 : and thus the places where this happened.
1004 : Expand enodes at program points where we hit the per-enode limit, so we
1005 : can investigate what exploded. */
1006 0 : const per_program_point_data *per_point_data
1007 0 : = m_eg.get_per_program_point_data (enode.get_point ());
1008 0 : return per_point_data->m_excess_enodes > 0;
1009 : }
1010 :
1011 : /* class exploded_node : public dnode<eg_traits>. */
1012 :
1013 : const char *
1014 0 : exploded_node::status_to_str (enum status s)
1015 : {
1016 0 : switch (s)
1017 : {
1018 0 : default: gcc_unreachable ();
1019 : case status::worklist: return "worklist";
1020 0 : case status::processed: return "processed";
1021 0 : case status::special: return "special";
1022 0 : case status::merger: return "merger";
1023 0 : case status::bulk_merged: return "bulk_merged";
1024 : }
1025 : }
1026 :
1027 : /* exploded_node's ctor. */
1028 :
1029 392414 : exploded_node::exploded_node (const point_and_state &ps,
1030 392414 : int index)
1031 392414 : : m_ps (ps), m_status (status::worklist), m_index (index),
1032 392414 : m_num_processed_stmts (0)
1033 : {
1034 392414 : gcc_checking_assert (ps.get_state ().m_region_model->canonicalized_p ());
1035 392414 : }
1036 :
1037 : /* For use by dump_dot, get a value for the .dot "fillcolor" attribute.
1038 : Colorize by sm-state, to make it easier to see how sm-state propagates
1039 : through the exploded_graph. */
1040 :
1041 : const char *
1042 1014 : exploded_node::get_dot_fillcolor () const
1043 : {
1044 1014 : const program_state &state = get_state ();
1045 :
1046 : /* We want to be able to easily distinguish the no-sm-state case,
1047 : and to be able to distinguish cases where there's a single state
1048 : from each other.
1049 :
1050 : Sum the sm_states, and use the result to choose from a table,
1051 : modulo table-size, special-casing the "no sm-state" case. */
1052 1014 : int total_sm_state = 0;
1053 1014 : int i;
1054 1014 : sm_state_map *smap;
1055 8112 : FOR_EACH_VEC_ELT (state.m_checker_states, i, smap)
1056 : {
1057 8450 : for (sm_state_map::iterator_t iter = smap->begin ();
1058 8450 : iter != smap->end ();
1059 1352 : ++iter)
1060 1352 : total_sm_state += (*iter).second.m_state->get_id ();
1061 7098 : total_sm_state += smap->get_global_state ()->get_id ();
1062 : }
1063 :
1064 1014 : if (total_sm_state > 0)
1065 : {
1066 : /* An arbitrarily-picked collection of light colors. */
1067 752 : const char * const colors[]
1068 : = {"azure", "coral", "cornsilk", "lightblue", "yellow",
1069 : "honeydew", "lightpink", "lightsalmon", "palegreen1",
1070 : "wheat", "seashell"};
1071 752 : const int num_colors = ARRAY_SIZE (colors);
1072 752 : return colors[total_sm_state % num_colors];
1073 : }
1074 : else
1075 : /* No sm-state. */
1076 : return "lightgrey";
1077 : }
1078 :
1079 : /* Implementation of dnode::dump_dot vfunc for exploded_node. */
1080 :
1081 : void
1082 507 : exploded_node::dump_dot (graphviz_out *gv, const dump_args_t &args) const
1083 : {
1084 507 : pretty_printer *pp = gv->get_pp ();
1085 :
1086 507 : dump_dot_id (pp);
1087 507 : pp_printf (pp, " [shape=none,margin=0,style=filled,fillcolor=%s,label=\"",
1088 : get_dot_fillcolor ());
1089 507 : pp_write_text_to_stream (pp);
1090 :
1091 507 : pp_printf (pp, "EN: %i", m_index);
1092 507 : if (m_status == status::merger)
1093 12 : pp_string (pp, " (merger)");
1094 495 : else if (m_status == status::bulk_merged)
1095 0 : pp_string (pp, " (bulk merged)");
1096 507 : pp_newline (pp);
1097 :
1098 507 : if (args.show_enode_details_p (*this))
1099 : {
1100 507 : format f (true);
1101 507 : m_ps.get_point ().print (pp, f);
1102 507 : pp_newline (pp);
1103 :
1104 507 : bool show_state = true;
1105 :
1106 : /* Don't show the state if we have a single predecessor
1107 : and the state hasn't changed. */
1108 507 : if (m_preds.length () == 1
1109 499 : && get_state () == m_preds[0]->m_src->get_state ())
1110 : show_state = false;
1111 :
1112 393 : if (show_state)
1113 : {
1114 393 : const extrinsic_state &ext_state = args.m_eg.get_ext_state ();
1115 393 : const program_state &state = m_ps.get_state ();
1116 393 : state.dump_to_pp (ext_state, false, true, pp);
1117 393 : pp_newline (pp);
1118 : }
1119 : }
1120 :
1121 507 : dump_saved_diagnostics (pp);
1122 :
1123 507 : args.dump_extra_info (this, pp);
1124 :
1125 507 : pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
1126 :
1127 507 : pp_string (pp, "\"];\n\n");
1128 :
1129 : /* It can be hard to locate the saved diagnostics as text within the
1130 : enode nodes, so add extra nodes to the graph for each saved_diagnostic,
1131 : highlighted in red.
1132 : Compare with dump_saved_diagnostics. */
1133 507 : {
1134 507 : unsigned i;
1135 507 : const saved_diagnostic *sd;
1136 1022 : FOR_EACH_VEC_ELT (m_saved_diagnostics, i, sd)
1137 : {
1138 8 : sd->dump_as_dot_node (pp);
1139 :
1140 : /* Add edge connecting this enode to the saved_diagnostic. */
1141 8 : dump_dot_id (pp);
1142 8 : pp_string (pp, " -> ");
1143 8 : sd->dump_dot_id (pp);
1144 8 : pp_string (pp, " [style=\"dotted\" arrowhead=\"none\"];");
1145 8 : pp_newline (pp);
1146 : }
1147 : }
1148 :
1149 507 : pp_flush (pp);
1150 507 : }
1151 :
1152 : /* Dump any saved_diagnostics at this enode to PP. */
1153 :
1154 : void
1155 581 : exploded_node::dump_saved_diagnostics (pretty_printer *pp) const
1156 : {
1157 581 : unsigned i;
1158 581 : const saved_diagnostic *sd;
1159 593 : FOR_EACH_VEC_ELT (m_saved_diagnostics, i, sd)
1160 : {
1161 12 : pp_printf (pp, "DIAGNOSTIC: %s (sd: %i)",
1162 12 : sd->m_d->get_kind (), sd->get_index ());
1163 12 : pp_newline (pp);
1164 : }
1165 581 : }
1166 :
1167 : /* Dump this to PP in a form suitable for use as an id in .dot output. */
1168 :
1169 : void
1170 1545 : exploded_node::dump_dot_id (pretty_printer *pp) const
1171 : {
1172 1545 : pp_printf (pp, "exploded_node_%i", m_index);
1173 1545 : }
1174 :
1175 : /* Dump a multiline representation of this node to PP. */
1176 :
1177 : void
1178 0 : exploded_node::dump_to_pp (pretty_printer *pp,
1179 : const extrinsic_state &ext_state) const
1180 : {
1181 0 : pp_printf (pp, "EN: %i", m_index);
1182 0 : pp_newline (pp);
1183 :
1184 0 : format f (true);
1185 0 : m_ps.get_point ().print (pp, f);
1186 0 : pp_newline (pp);
1187 :
1188 0 : m_ps.get_state ().dump_to_pp (ext_state, false, true, pp);
1189 0 : pp_newline (pp);
1190 0 : }
1191 :
1192 : /* Dump a multiline representation of this node to FILE. */
1193 :
1194 : void
1195 0 : exploded_node::dump (FILE *fp,
1196 : const extrinsic_state &ext_state) const
1197 : {
1198 0 : tree_dump_pretty_printer pp (fp);
1199 0 : dump_to_pp (&pp, ext_state);
1200 0 : }
1201 :
1202 : /* Dump a multiline representation of this node to stderr. */
1203 :
1204 : DEBUG_FUNCTION void
1205 0 : exploded_node::dump (const extrinsic_state &ext_state) const
1206 : {
1207 0 : dump (stderr, ext_state);
1208 0 : }
1209 :
1210 : /* Return a new json::object of the form
1211 : {"point" : object for program_point,
1212 : "state" : object for program_state,
1213 : "status" : str,
1214 : "idx" : int,
1215 : "processed_stmts" : int}. */
1216 :
1217 : std::unique_ptr<json::object>
1218 0 : exploded_node::to_json (const extrinsic_state &ext_state) const
1219 : {
1220 0 : auto enode_obj = std::make_unique<json::object> ();
1221 :
1222 0 : enode_obj->set ("point", get_point ().to_json ());
1223 0 : enode_obj->set ("state", get_state ().to_json (ext_state));
1224 0 : enode_obj->set_string ("status", status_to_str (m_status));
1225 0 : enode_obj->set_integer ("idx", m_index);
1226 0 : enode_obj->set_integer ("processed_stmts", m_num_processed_stmts);
1227 :
1228 0 : return enode_obj;
1229 : }
1230 :
1231 : } // namespace ana
1232 :
1233 : /* Return true if FNDECL has a gimple body. */
1234 : // TODO: is there a pre-canned way to do this?
1235 :
1236 : bool
1237 18345 : fndecl_has_gimple_body_p (tree fndecl)
1238 : {
1239 18345 : if (fndecl == NULL_TREE)
1240 : return false;
1241 :
1242 18345 : cgraph_node *n = cgraph_node::get (fndecl);
1243 18345 : if (!n)
1244 : return false;
1245 :
1246 18345 : return n->has_gimple_body_p ();
1247 : }
1248 :
1249 : namespace ana {
1250 :
1251 : /* Subclass of call_info for exploded edges that express
1252 : a throw or rethrow of an exception (actually a call
1253 : to __cxa_throw or __cxa_rethrow). */
1254 :
1255 : class throw_custom_edge : public call_info
1256 : {
1257 : public:
1258 187 : throw_custom_edge (const call_details &cd,
1259 : tree type,
1260 : bool is_rethrow)
1261 187 : : call_info (cd),
1262 187 : m_type (type),
1263 187 : m_is_rethrow (is_rethrow)
1264 : {
1265 : }
1266 :
1267 0 : void print (pretty_printer *pp) const final override
1268 : {
1269 0 : if (m_is_rethrow)
1270 : {
1271 0 : if (m_type)
1272 0 : pp_printf (pp, "rethrowing %qT", m_type);
1273 : else
1274 0 : pp_printf (pp, "rethrowing");
1275 : }
1276 : else
1277 : {
1278 0 : if (m_type)
1279 0 : pp_printf (pp, "throwing %qT", m_type);
1280 : else
1281 0 : pp_printf (pp, "throwing");
1282 : }
1283 0 : }
1284 :
1285 0 : void print_desc (pretty_printer &pp) const final override
1286 : {
1287 0 : print (&pp);
1288 0 : }
1289 :
1290 343 : bool update_model (region_model *model,
1291 : const exploded_edge *,
1292 : region_model_context *ctxt) const final override
1293 : {
1294 343 : if (m_is_rethrow)
1295 : {
1296 117 : if (auto eh_node = model->get_current_caught_exception ())
1297 84 : model->push_thrown_exception (*eh_node);
1298 : else
1299 : {
1300 : /* We have a rethrow of some unknown exception.
1301 : We don't have a good way of representing this;
1302 : leave the exception stack empty. */
1303 : }
1304 : }
1305 : else
1306 : {
1307 226 : call_details cd (get_call_details (model, ctxt));
1308 :
1309 226 : const svalue *exception_sval = cd.get_arg_svalue (0);
1310 226 : const svalue *tinfo_sval = cd.get_arg_svalue (1);
1311 226 : const svalue *destructor_sval = cd.get_arg_svalue (2);
1312 :
1313 : /* Push a new exception_node on the model's m_exception_stack. */
1314 226 : exception_node eh_node (exception_sval, tinfo_sval, destructor_sval);
1315 226 : model->push_thrown_exception (eh_node);
1316 : }
1317 :
1318 343 : return true;
1319 : }
1320 :
1321 69 : void add_events_to_path (checker_path *emission_path,
1322 : const exploded_edge &eedge,
1323 : pending_diagnostic &,
1324 : const state_transition *) const final override
1325 : {
1326 69 : const exploded_node *dst_node = eedge.m_dest;
1327 69 : const program_point &dst_point = dst_node->get_point ();
1328 69 : const int dst_stack_depth = dst_point.get_stack_depth ();
1329 :
1330 69 : const gcall &call = get_call_stmt ();
1331 :
1332 69 : emission_path->add_event
1333 69 : (std::make_unique<explicit_throw_event>
1334 69 : (event_loc_info (call.location,
1335 : dst_point.get_fndecl (),
1336 69 : dst_stack_depth),
1337 : dst_node,
1338 : call,
1339 69 : m_type,
1340 69 : m_is_rethrow));
1341 69 : }
1342 :
1343 : private:
1344 : tree m_type;
1345 : bool m_is_rethrow;
1346 : };
1347 :
1348 : /* Subclass of custom_edge_info for an exploded edge that expresses
1349 : unwinding one stack frame during exception handling. */
1350 :
1351 : class unwind_custom_edge : public custom_edge_info
1352 : {
1353 : public:
1354 6434 : unwind_custom_edge (location_t loc)
1355 6434 : : m_loc (loc)
1356 : {
1357 : }
1358 :
1359 0 : void print (pretty_printer *pp) const final override
1360 : {
1361 0 : pp_printf (pp, "unwinding frame");
1362 0 : }
1363 :
1364 6466 : bool update_model (region_model *model,
1365 : const exploded_edge *,
1366 : region_model_context *ctxt) const final override
1367 : {
1368 6466 : model->pop_frame (NULL_TREE, nullptr, ctxt, nullptr, false);
1369 6466 : return true;
1370 : }
1371 :
1372 16 : void add_events_to_path (checker_path *emission_path,
1373 : const exploded_edge &eedge,
1374 : pending_diagnostic &,
1375 : const state_transition *) const final override
1376 : {
1377 16 : const exploded_node *src_node = eedge.m_src;
1378 16 : const program_point &src_point = src_node->get_point ();
1379 16 : const int src_stack_depth = src_point.get_stack_depth ();
1380 16 : emission_path->add_event
1381 32 : (std::make_unique<unwind_event> (event_loc_info (m_loc,
1382 : src_point.get_fndecl (),
1383 16 : src_stack_depth)));
1384 16 : }
1385 :
1386 : private:
1387 : location_t m_loc;
1388 : };
1389 :
1390 : /* Locate an SNODE that's a CFG edge with the EH flag,
1391 : or return nullptr. */
1392 :
1393 : static const superedge *
1394 7263 : get_eh_outedge (const supernode &snode)
1395 : {
1396 26654 : for (auto out_sedge : snode.m_succs)
1397 6766 : if (::edge cfg_edge = out_sedge->get_any_cfg_edge ())
1398 1258 : if (cfg_edge->flags & EDGE_EH)
1399 : return out_sedge;
1400 :
1401 : // Not found
1402 : return nullptr;
1403 : }
1404 :
1405 : /* Given THROWN_ENODE, which expreses a throw or rethrow occurring at
1406 : THROW_STMT, unwind intraprocedurally and interprocedurally to find
1407 : the next eh_dispatch statement to handle exceptions, if any.
1408 :
1409 : Add eedges and enodes to this graph expressing the actions taken
1410 : to reach an enode containing the eh_dispatch stmt, if any.
1411 : Only the final enode is added to this graph's worklist.
1412 :
1413 : Use CTXT to warn about problems e.g. memory leaks due to stack frames
1414 : being unwound. */
1415 :
1416 : void
1417 6086 : exploded_graph::unwind_from_exception (exploded_node &thrown_enode,
1418 : const gimple *throw_stmt,
1419 : region_model_context *ctxt)
1420 : {
1421 6086 : logger * const logger = get_logger ();
1422 6086 : LOG_FUNC_1 (logger, "thrown EN: %i", thrown_enode.m_index);
1423 :
1424 : /* Iteratively unwind the stack looking for an out-cfg-edge
1425 : flagged EH. */
1426 6086 : exploded_node *iter_enode = &thrown_enode;
1427 6086 : while (iter_enode)
1428 : {
1429 : /* If we have an out-cfg-edge flagged EH, follow that,
1430 : presumably to a bb with a label and an eh_dispatch stmt.
1431 : Otherwise assume no out-cfgs-edges, and we are unwinding to the
1432 : caller. */
1433 7263 : if (auto sedge = get_eh_outedge (*iter_enode->get_supernode ()))
1434 : {
1435 : /* Intraprocedural case.
1436 : Assume we have an out-edge flagged with EH leading to
1437 : code for dispatch to catch handlers. */
1438 829 : const program_point next_point
1439 829 : (sedge->m_dest,
1440 829 : iter_enode->get_point ().get_call_string ());
1441 829 : exploded_node *next_enode
1442 829 : = get_or_create_node (next_point,
1443 : iter_enode->get_state (),
1444 : iter_enode,
1445 : /* Add this enode to the worklist. */
1446 : true);
1447 829 : if (!next_enode)
1448 : return;
1449 :
1450 829 : add_edge (iter_enode, next_enode, nullptr, false, nullptr);
1451 829 : return;
1452 : }
1453 : else
1454 : {
1455 : /* Interprocedural case.
1456 : No out-cfg-edge. Unwind one stack frame. */
1457 6434 : program_state unwound_state (iter_enode->get_state ());
1458 6434 : location_t loc = throw_stmt ? throw_stmt->location : UNKNOWN_LOCATION;
1459 6434 : auto unwind_edge_info
1460 6434 : = std::make_unique<unwind_custom_edge> (loc);
1461 6434 : unwind_edge_info->update_model (unwound_state.m_region_model, nullptr,
1462 : ctxt);
1463 :
1464 : /* Detect leaks in the new state relative to the old state.
1465 : Use an alternate ctxt that uses the original enode and the stmt
1466 : (if any) for the location of any diagnostics. */
1467 6434 : {
1468 6434 : uncertainty_t uncertainty;
1469 6434 : impl_region_model_context ctxt (*this,
1470 : &thrown_enode,
1471 6434 : &iter_enode->get_state (),
1472 : &unwound_state,
1473 : &uncertainty,
1474 : nullptr,
1475 6434 : throw_stmt);
1476 6434 : program_state::detect_leaks (iter_enode->get_state (),
1477 : unwound_state,
1478 : nullptr,
1479 : get_ext_state (), &ctxt);
1480 6434 : }
1481 6434 : const call_string &cs = iter_enode->get_point ().get_call_string ();
1482 6434 : if (cs.empty_p ())
1483 : {
1484 : /* Top-level stack frame in analysis: unwinding
1485 : to the outside world that called us. */
1486 : return;
1487 : }
1488 : else
1489 : {
1490 : /* Nested function in analysis: unwinding to
1491 : the callsite in the analysis (or beyond). */
1492 1593 : program_point unwound_point (cs.get_return_node_in_caller (), cs);
1493 1593 : unwound_point.pop_from_call_stack ();
1494 :
1495 1593 : exploded_node *after_unwind_enode
1496 1593 : = get_or_create_node (unwound_point,
1497 : std::move (unwound_state),
1498 : iter_enode,
1499 : /* Don't add this enode to the
1500 : worklist; we will process it
1501 : on the next iteration. */
1502 : false);
1503 :
1504 1593 : if (!after_unwind_enode)
1505 416 : return;
1506 :
1507 1177 : add_edge (iter_enode, after_unwind_enode, nullptr, true,
1508 1177 : std::move (unwind_edge_info));
1509 1177 : iter_enode = after_unwind_enode;
1510 : }
1511 6434 : }
1512 : }
1513 6086 : }
1514 :
1515 : /* Handle THROW_CALL, a call to __cxa_throw or __cxa_rethrow.
1516 :
1517 : Create an eedge and destination enode for the throw/rethrow, adding
1518 : them to this egraph. The new enode isn't added to the worklist, but
1519 : instead exploded_graph::unwind_from_exception is immediately called
1520 : on it, potentially creating more eedges and enodes leading to an
1521 : eh_handler stmt. */
1522 :
1523 : void
1524 190 : exploded_node::on_throw (exploded_graph &eg,
1525 : const gcall &throw_call,
1526 : const program_point &after_throw_point,
1527 : program_state *new_state,
1528 : bool is_rethrow,
1529 : region_model_context *ctxt)
1530 : {
1531 190 : region_model *model = new_state->m_region_model;
1532 190 : call_details cd (throw_call, model, ctxt);
1533 :
1534 190 : if (!cd.get_fndecl_for_call ())
1535 3 : return;
1536 :
1537 : /* Create an enode and eedge for the "throw". */
1538 187 : tree type = NULL_TREE;
1539 187 : if (is_rethrow)
1540 : {
1541 81 : const exception_node *eh_node = model->get_current_caught_exception ();
1542 66 : if (eh_node)
1543 66 : type = eh_node->maybe_get_type ();
1544 : else
1545 : {
1546 : /* We have a "throw;" but no exception to rethrow.
1547 : Presumably the top-level of the analysis is an
1548 : entrypoint for handling exceptions, so we will
1549 : simulate fully unwinding. */
1550 : }
1551 : }
1552 : else
1553 : {
1554 106 : const svalue *tinfo_sval = cd.get_arg_svalue (1);
1555 106 : type = tinfo_sval->maybe_get_type_from_typeinfo ();
1556 : }
1557 :
1558 187 : auto throw_edge_info
1559 187 : = std::make_unique<throw_custom_edge> (cd, type, is_rethrow);
1560 187 : throw_edge_info->update_model (model, nullptr, ctxt);
1561 :
1562 187 : exploded_node *after_throw_enode
1563 187 : = eg.get_or_create_node (after_throw_point, *new_state, this,
1564 : /* Don't add to worklist; we process
1565 : this immediately below. */
1566 : false);
1567 :
1568 187 : if (!after_throw_enode)
1569 0 : return;
1570 :
1571 : /* Create custom exploded_edge for a throw. */
1572 187 : eg.add_edge (this, after_throw_enode, nullptr, true,
1573 187 : std::move (throw_edge_info));
1574 :
1575 187 : eg.unwind_from_exception (*after_throw_enode, &throw_call, ctxt);
1576 187 : }
1577 :
1578 : /* Subroutine of exploded_graph::process_node for finding the successors
1579 : of the supernode for a function exit basic block.
1580 :
1581 : Ensure that pop_frame is called, potentially queuing diagnostics about
1582 : leaks. */
1583 :
1584 : void
1585 11453 : exploded_node::detect_leaks (exploded_graph &eg)
1586 : {
1587 11453 : LOG_FUNC_1 (eg.get_logger (), "EN: %i", m_index);
1588 :
1589 11453 : gcc_assert (get_point ().get_supernode ()->exit_p ());
1590 :
1591 : /* If we're not a "top-level" function, do nothing; pop_frame
1592 : will be called when handling the return superedge. */
1593 11453 : if (get_point ().get_stack_depth () > 1)
1594 0 : return;
1595 :
1596 : /* We have a "top-level" function. */
1597 11453 : gcc_assert (get_point ().get_stack_depth () == 1);
1598 :
1599 11453 : const program_state &old_state = get_state ();
1600 :
1601 : /* Work with a temporary copy of the state: pop the frame, and see
1602 : what leaks (via purge_unused_svalues). */
1603 11453 : program_state new_state (old_state);
1604 :
1605 11453 : gcc_assert (new_state.m_region_model);
1606 :
1607 11453 : uncertainty_t uncertainty;
1608 11453 : impl_region_model_context ctxt (eg, this,
1609 : &old_state, &new_state, &uncertainty, nullptr,
1610 11453 : nullptr);
1611 11453 : const svalue *result = nullptr;
1612 11453 : new_state.m_region_model->pop_frame (nullptr, &result, &ctxt, nullptr);
1613 11453 : program_state::detect_leaks (old_state, new_state, result,
1614 : eg.get_ext_state (), &ctxt);
1615 22906 : }
1616 :
1617 : /* Dump the successors and predecessors of this enode to OUTF. */
1618 :
1619 : void
1620 0 : exploded_node::dump_succs_and_preds (FILE *outf) const
1621 : {
1622 0 : unsigned i;
1623 0 : exploded_edge *e;
1624 0 : {
1625 0 : auto_vec<exploded_node *> preds (m_preds.length ());
1626 0 : FOR_EACH_VEC_ELT (m_preds, i, e)
1627 0 : preds.quick_push (e->m_src);
1628 0 : pretty_printer pp;
1629 0 : print_enode_indices (&pp, preds);
1630 0 : fprintf (outf, "preds: %s\n",
1631 : pp_formatted_text (&pp));
1632 0 : }
1633 0 : {
1634 0 : auto_vec<exploded_node *> succs (m_succs.length ());
1635 0 : FOR_EACH_VEC_ELT (m_succs, i, e)
1636 0 : succs.quick_push (e->m_dest);
1637 0 : pretty_printer pp;
1638 0 : print_enode_indices (&pp, succs);
1639 0 : fprintf (outf, "succs: %s\n",
1640 : pp_formatted_text (&pp));
1641 0 : }
1642 0 : }
1643 :
1644 : // class interprocedural_call : public custom_edge_info
1645 :
1646 : void
1647 8 : interprocedural_call::print (pretty_printer *pp) const
1648 : {
1649 8 : pp_string (pp, "call to ");
1650 8 : pp_gimple_stmt_1 (pp, &get_gcall (), 0, (dump_flags_t)0);
1651 8 : }
1652 :
1653 : void
1654 8 : interprocedural_call::get_dot_attrs (const char *&/*out_style*/,
1655 : const char *&out_color) const
1656 : {
1657 8 : out_color = "red";
1658 8 : }
1659 :
1660 : bool
1661 6830 : interprocedural_call::update_state (program_state *state,
1662 : const exploded_edge *eedge,
1663 : region_model_context *ctxt) const
1664 : {
1665 6830 : return update_model (state->m_region_model, eedge, ctxt);
1666 : }
1667 :
1668 : bool
1669 12747 : interprocedural_call::update_model (region_model *model,
1670 : const exploded_edge */*eedge*/,
1671 : region_model_context *ctxt) const
1672 : {
1673 12747 : model->update_for_gcall (get_gcall (), ctxt, &m_callee_fun);
1674 12747 : return true;
1675 : }
1676 :
1677 : void
1678 1241 : interprocedural_call::add_events_to_path (checker_path *emission_path,
1679 : const exploded_edge &eedge,
1680 : pending_diagnostic &pd,
1681 : const state_transition *state_trans) const
1682 : {
1683 1264 : pd.add_call_event (eedge, get_gcall (), *emission_path,
1684 : (state_trans
1685 23 : ? state_trans->dyn_cast_state_transition_at_call ()
1686 : : nullptr));
1687 1241 : }
1688 :
1689 : bool
1690 1087 : interprocedural_call::try_to_rewind_data_flow (rewind_context &ctxt) const
1691 : {
1692 1087 : auto logger = ctxt.m_logger;
1693 :
1694 : // Rewind from params to arguments
1695 1087 : if (ctxt.m_input.m_region_holding_value)
1696 : {
1697 57 : const region_model &dst_enode_model = ctxt.get_dst_region_model ();
1698 57 : tree dst_tree
1699 : = dst_enode_model.get_representative_tree
1700 57 : (ctxt.m_input.m_region_holding_value);
1701 57 : if (dst_tree)
1702 : {
1703 52 : callsite_expr expr;
1704 52 : tree src_tree
1705 52 : = m_op.map_expr_from_callee_to_caller (m_callee_fun.decl,
1706 : dst_tree,
1707 : &expr);
1708 52 : if (src_tree)
1709 : {
1710 23 : const region_model &src_enode_model = ctxt.get_src_region_model ();
1711 23 : ctxt.m_output.m_region_holding_value
1712 23 : = src_enode_model.get_lvalue (src_tree, nullptr);
1713 :
1714 23 : ctxt.add_state_transition
1715 23 : (std::make_unique<state_transition_at_call> (expr));
1716 :
1717 23 : if (logger)
1718 : {
1719 0 : callsite_expr_element e (expr);
1720 0 : logger->log ("updating m_region_holding_value from %qE to %qE"
1721 : " (callsite_expr: %e)",
1722 : dst_tree, src_tree, &e);
1723 0 : }
1724 : }
1725 : }
1726 : }
1727 :
1728 1087 : return true;
1729 : }
1730 :
1731 : const gcall &
1732 13996 : interprocedural_call::get_gcall () const
1733 : {
1734 13996 : return m_op.get_gcall ();
1735 : }
1736 :
1737 : // class interprocedural_return : public custom_edge_info
1738 :
1739 : void
1740 16 : interprocedural_return::print (pretty_printer *pp) const
1741 : {
1742 16 : pp_string (pp, "return from ");
1743 16 : pp_gimple_stmt_1 (pp, &m_call_stmt, 0, (dump_flags_t)0);
1744 16 : }
1745 :
1746 : void
1747 8 : interprocedural_return::get_dot_attrs (const char *&/*out_style*/,
1748 : const char *&out_color) const
1749 : {
1750 8 : out_color = "green";
1751 8 : }
1752 :
1753 : bool
1754 5816 : interprocedural_return::update_state (program_state *state,
1755 : const exploded_edge *eedge,
1756 : region_model_context *ctxt) const
1757 : {
1758 5816 : return update_model (state->m_region_model, eedge, ctxt);
1759 : }
1760 :
1761 : bool
1762 9042 : interprocedural_return::update_model (region_model *model,
1763 : const exploded_edge */*eedge*/,
1764 : region_model_context *ctxt) const
1765 : {
1766 9042 : model->update_for_return_gcall (m_call_stmt, ctxt);
1767 9042 : return true;
1768 : }
1769 :
1770 : void
1771 631 : interprocedural_return::add_events_to_path (checker_path *emission_path,
1772 : const exploded_edge &eedge,
1773 : pending_diagnostic &,
1774 : const state_transition *state_trans) const
1775 : {
1776 631 : const program_point &dst_point = eedge.m_dest->get_point ();
1777 631 : emission_path->add_event
1778 631 : (std::make_unique<return_event>
1779 631 : (eedge,
1780 1262 : event_loc_info (m_call_stmt.location,
1781 : dst_point.get_fndecl (),
1782 1262 : dst_point.get_stack_depth ()),
1783 : (state_trans
1784 631 : ? state_trans->dyn_cast_state_transition_at_return ()
1785 : : nullptr)));
1786 631 : }
1787 :
1788 : bool
1789 565 : interprocedural_return::try_to_rewind_data_flow (rewind_context &ctxt) const
1790 : {
1791 565 : auto logger = ctxt.m_logger;
1792 :
1793 565 : tree lhs = gimple_call_lhs (&m_call_stmt);
1794 565 : if (!lhs)
1795 : return true;
1796 :
1797 341 : const region_model &src_enode_model = ctxt.get_src_region_model ();
1798 341 : tree fndecl = src_enode_model.get_current_function ()->decl;
1799 341 : tree fn_result = DECL_RESULT (fndecl);
1800 :
1801 341 : ctxt.on_data_flow (DECL_RESULT (fndecl), lhs);
1802 :
1803 341 : return true;
1804 : }
1805 :
1806 : /* class exploded_edge : public dedge<eg_traits>. */
1807 :
1808 : /* exploded_edge's ctor. */
1809 :
1810 406557 : exploded_edge::exploded_edge (exploded_node *src, exploded_node *dest,
1811 : const superedge *sedge, bool could_do_work,
1812 406557 : std::unique_ptr<custom_edge_info> custom_info)
1813 406557 : : dedge<eg_traits> (src, dest), m_sedge (sedge),
1814 406557 : m_custom_info (std::move (custom_info)),
1815 406557 : m_could_do_work_p (could_do_work)
1816 : {
1817 406557 : }
1818 :
1819 : /* Implementation of dedge::dump_dot vfunc for exploded_edge.
1820 : Use the label of the underlying superedge, if any. */
1821 :
1822 : void
1823 515 : exploded_edge::dump_dot (graphviz_out *gv, const dump_args_t &) const
1824 : {
1825 515 : pretty_printer *pp = gv->get_pp ();
1826 :
1827 515 : m_src->dump_dot_id (pp);
1828 515 : pp_string (pp, " -> ");
1829 515 : m_dest->dump_dot_id (pp);
1830 515 : dump_dot_label (pp);
1831 515 : }
1832 :
1833 : /* Second half of exploded_edge::dump_dot. This is split out
1834 : for use by trimmed_graph::dump_dot and base_feasible_edge::dump_dot. */
1835 :
1836 : void
1837 585 : exploded_edge::dump_dot_label (pretty_printer *pp) const
1838 : {
1839 585 : const char *style = "\"solid,bold\"";
1840 585 : const char *color = "black";
1841 585 : int weight = 10;
1842 585 : const char *constraint = "true";
1843 :
1844 585 : if (m_sedge)
1845 : {
1846 539 : if (m_sedge->get_op ())
1847 426 : style = "\"solid\"";
1848 : else
1849 113 : style = "\"dotted\"";
1850 : }
1851 585 : if (m_custom_info)
1852 22 : m_custom_info->get_dot_attrs (style, color);
1853 :
1854 585 : pp_printf (pp,
1855 : (" [style=%s, color=%s, weight=%d, constraint=%s,"
1856 : " headlabel=\""),
1857 : style, color, weight, constraint);
1858 585 : pp_flush (pp);
1859 :
1860 585 : if (m_sedge)
1861 539 : m_sedge->dump_label_to_pp (pp, false);
1862 46 : else if (m_custom_info)
1863 14 : m_custom_info->print (pp);
1864 :
1865 1132 : pp_printf (pp, "%s",
1866 585 : could_do_work_p () ? "(could do work)" : "DOES NO WORK");
1867 :
1868 585 : pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false);
1869 :
1870 585 : pp_printf (pp, "\"];\n");
1871 585 : }
1872 :
1873 : /* Return a new json::object of the form
1874 : {"src_idx": int, the index of the source exploded edge,
1875 : "dst_idx": int, the index of the destination exploded edge,
1876 : "sedge": (optional) object for the superedge, if any,
1877 : "custom": (optional) str, a description, if this is a custom edge}. */
1878 :
1879 : std::unique_ptr<json::object>
1880 0 : exploded_edge::to_json () const
1881 : {
1882 0 : auto eedge_obj = std::make_unique<json::object> ();
1883 0 : eedge_obj->set_integer ("src_idx", m_src->m_index);
1884 0 : eedge_obj->set_integer ("dst_idx", m_dest->m_index);
1885 0 : if (m_sedge)
1886 0 : eedge_obj->set ("sedge", m_sedge->to_json ());
1887 0 : if (m_custom_info)
1888 : {
1889 0 : pretty_printer pp;
1890 0 : pp_format_decoder (&pp) = default_tree_printer;
1891 0 : m_custom_info->print (&pp);
1892 0 : eedge_obj->set_string ("custom", pp_formatted_text (&pp));
1893 0 : }
1894 0 : return eedge_obj;
1895 : }
1896 :
1897 : const gimple *
1898 18224 : exploded_edge::maybe_get_stmt () const
1899 : {
1900 18224 : auto op = maybe_get_op ();
1901 18224 : if (!op)
1902 : return nullptr;
1903 14017 : return op->maybe_get_stmt ();
1904 : }
1905 :
1906 : const operation *
1907 57475 : exploded_edge::maybe_get_op () const
1908 : {
1909 57475 : if (!m_sedge)
1910 : return nullptr;
1911 52417 : return m_sedge->get_op ();
1912 : }
1913 :
1914 : /* struct stats. */
1915 :
1916 : /* stats' ctor. */
1917 :
1918 25811 : stats::stats (int num_supernodes)
1919 25811 : : m_num_nodes (0),
1920 25811 : m_node_reuse_count (0),
1921 25811 : m_node_reuse_after_merge_count (0),
1922 25811 : m_num_supernodes (num_supernodes)
1923 : {
1924 25811 : }
1925 :
1926 : /* Log these stats in multiline form to LOGGER. */
1927 :
1928 : void
1929 11 : stats::log (logger *logger) const
1930 : {
1931 11 : gcc_assert (logger);
1932 11 : logger->log ("m_num_nodes: %i", m_num_nodes);
1933 11 : logger->log ("m_node_reuse_count: %i", m_node_reuse_count);
1934 11 : logger->log ("m_node_reuse_after_merge_count: %i",
1935 11 : m_node_reuse_after_merge_count);
1936 11 : }
1937 :
1938 : /* Dump these stats in multiline form to OUT. */
1939 :
1940 : void
1941 0 : stats::dump (FILE *out) const
1942 : {
1943 0 : fprintf (out, "m_num_nodes: %i\n", m_num_nodes);
1944 0 : fprintf (out, "m_node_reuse_count: %i\n", m_node_reuse_count);
1945 0 : fprintf (out, "m_node_reuse_after_merge_count: %i\n",
1946 0 : m_node_reuse_after_merge_count);
1947 :
1948 0 : if (m_num_supernodes > 0)
1949 0 : fprintf (out, "enodes per supernode: %.2f\n",
1950 0 : (float)m_num_nodes / (float)m_num_supernodes);
1951 0 : }
1952 :
1953 : /* Return the total number of enodes recorded within this object. */
1954 :
1955 : int
1956 6 : stats::get_total_enodes () const
1957 : {
1958 6 : return m_num_nodes;
1959 : }
1960 :
1961 : /* struct per_function_data. */
1962 :
1963 367 : per_function_data::~per_function_data ()
1964 : {
1965 1730 : for (auto iter : m_summaries)
1966 629 : delete iter;
1967 367 : }
1968 :
1969 : void
1970 629 : per_function_data::add_call_summary (exploded_node *node)
1971 : {
1972 629 : m_summaries.safe_push (new call_summary (this, node));
1973 629 : }
1974 :
1975 : /* strongly_connected_components's ctor. Tarjan's SCC algorithm. */
1976 :
1977 3423 : strongly_connected_components::
1978 3423 : strongly_connected_components (const supergraph &sg, logger *logger)
1979 6842 : : m_sg (sg), m_per_node (m_sg.m_nodes.length ())
1980 : {
1981 3423 : LOG_SCOPE (logger);
1982 3423 : auto_timevar tv (TV_ANALYZER_SCC);
1983 :
1984 187401 : for (size_t i = 0; i < m_sg.m_nodes.length (); i++)
1985 183978 : m_per_node.quick_push (per_node_data ());
1986 :
1987 194239 : for (auto snode : m_sg.m_nodes)
1988 183978 : if (m_per_node[snode->m_id].m_id == -1)
1989 10415 : strong_connect (snode->m_id, logger);
1990 :
1991 3423 : if (0)
1992 : dump ();
1993 3423 : }
1994 :
1995 : /* Dump this object to stderr. */
1996 :
1997 : DEBUG_FUNCTION void
1998 0 : strongly_connected_components::dump () const
1999 : {
2000 0 : fprintf (stderr, "Stack: [");
2001 0 : bool first = true;
2002 0 : for (auto i : m_stack)
2003 : {
2004 0 : if (first)
2005 : first = false;
2006 : else
2007 0 : fprintf (stderr, ", ");
2008 0 : fprintf (stderr, "%i", i);
2009 : }
2010 0 : fprintf (stderr, "]\n");
2011 0 : for (size_t i = 0; i < m_sg.m_nodes.length (); i++)
2012 : {
2013 0 : const per_node_data &v = m_per_node[i];
2014 0 : fprintf (stderr, "SN %lu: index: %i lowlink: %i on_stack: %i\n",
2015 0 : i, v.m_id, v.m_lowlink, v.m_on_stack);
2016 : }
2017 0 : }
2018 :
2019 : /* Return a new json::array of per-snode SCC ids. */
2020 :
2021 : std::unique_ptr<json::array>
2022 0 : strongly_connected_components::to_json () const
2023 : {
2024 0 : auto scc_arr = std::make_unique<json::array> ();
2025 0 : for (size_t i = 0; i < m_sg.m_nodes.length (); i++)
2026 0 : scc_arr->append (std::make_unique<json::integer_number> (get_scc_id (i)));
2027 0 : return scc_arr;
2028 : }
2029 :
2030 : /* Subroutine of strongly_connected_components's ctor, part of Tarjan's
2031 : SCC algorithm. */
2032 :
2033 : void
2034 183978 : strongly_connected_components::strong_connect (unsigned id,
2035 : logger *logger)
2036 : {
2037 183978 : supernode *v_snode = m_sg.m_nodes[id];
2038 183978 : if (!v_snode)
2039 183978 : return;
2040 :
2041 : /* Set the depth index for v to the smallest unused index. */
2042 183978 : per_node_data *v = &m_per_node[id];
2043 183978 : v->m_id = id;
2044 183978 : v->m_lowlink = id;
2045 183978 : m_stack.safe_push (id);
2046 183978 : v->m_on_stack = true;
2047 183978 : id++;
2048 :
2049 : /* Consider successors of v. */
2050 183978 : unsigned i;
2051 183978 : superedge *sedge;
2052 367640 : FOR_EACH_VEC_ELT (v_snode->m_succs, i, sedge)
2053 : {
2054 183662 : supernode *w_snode = sedge->m_dest;
2055 183662 : per_node_data *w = &m_per_node[w_snode->m_id];
2056 183662 : if (w->m_id == -1)
2057 : {
2058 : /* Successor w has not yet been visited; recurse on it. */
2059 173563 : strong_connect (w_snode->m_id, logger);
2060 173563 : v->m_lowlink = MIN (v->m_lowlink, w->m_lowlink);
2061 : }
2062 10099 : else if (w->m_on_stack)
2063 : {
2064 : /* Successor w is in stack S and hence in the current SCC
2065 : If w is not on stack, then (v, w) is a cross-edge in the DFS
2066 : tree and must be ignored. */
2067 2171 : v->m_lowlink = MIN (v->m_lowlink, w->m_id);
2068 : }
2069 : }
2070 :
2071 : /* If v is a root node, pop the stack and generate an SCC. */
2072 :
2073 183978 : if (v->m_lowlink == v->m_id)
2074 : {
2075 167748 : if (logger)
2076 50 : logger->log ("got SCC root node: SN %i", v->m_id);
2077 183978 : per_node_data *w;
2078 183978 : do {
2079 183978 : int id = m_stack.pop ();
2080 183978 : w = &m_per_node[id];
2081 183978 : w->m_on_stack = false;
2082 183978 : if (logger)
2083 80 : logger->log (" popping SN %i", w->m_id);
2084 183978 : } while (w != v);
2085 : }
2086 : }
2087 :
2088 : /* worklist's ctor. */
2089 :
2090 3423 : worklist::worklist (const exploded_graph &eg, const analysis_plan &plan)
2091 3423 : : m_scc (eg.get_supergraph (), eg.get_logger ()),
2092 3423 : m_plan (plan),
2093 3423 : m_queue (key_t (*this, nullptr))
2094 : {
2095 3423 : }
2096 :
2097 : /* Return the number of nodes in the worklist. */
2098 :
2099 : unsigned
2100 367533 : worklist::length () const
2101 : {
2102 367533 : return m_queue.nodes ();
2103 : }
2104 :
2105 : /* Return the next node in the worklist, removing it. */
2106 :
2107 : exploded_node *
2108 384185 : worklist::take_next ()
2109 : {
2110 384185 : return m_queue.extract_min ();
2111 : }
2112 :
2113 : /* Return the next node in the worklist without removing it. */
2114 :
2115 : exploded_node *
2116 404793 : worklist::peek_next ()
2117 : {
2118 404793 : return m_queue.min ();
2119 : }
2120 :
2121 : /* Add ENODE to the worklist. */
2122 :
2123 : void
2124 385226 : worklist::add_node (exploded_node *enode)
2125 : {
2126 385226 : gcc_assert (enode->get_status () == exploded_node::status::worklist);
2127 385226 : m_queue.insert (key_t (*this, enode), enode);
2128 385226 : }
2129 :
2130 : /* Comparator for implementing worklist::key_t comparison operators.
2131 : Return negative if KA is before KB
2132 : Return positive if KA is after KB
2133 : Return 0 if they are equal.
2134 :
2135 : The ordering of the worklist is critical for performance and for
2136 : avoiding node explosions. Ideally we want all enodes at a CFG join-point
2137 : with the same callstring to be sorted next to each other in the worklist
2138 : so that a run of consecutive enodes can be merged and processed "in bulk"
2139 : rather than individually or pairwise, minimizing the number of new enodes
2140 : created. */
2141 :
2142 : int
2143 1908474 : worklist::key_t::cmp (const worklist::key_t &ka, const worklist::key_t &kb)
2144 : {
2145 1908474 : const program_point &point_a = ka.m_enode->get_point ();
2146 1908474 : const program_point &point_b = kb.m_enode->get_point ();
2147 1908474 : const call_string &call_string_a = point_a.get_call_string ();
2148 1908474 : const call_string &call_string_b = point_b.get_call_string ();
2149 :
2150 : /* Order empty-callstring points with different functions based on the
2151 : analysis_plan, so that we generate summaries before they are used. */
2152 1908474 : if (flag_analyzer_call_summaries
2153 199654 : && call_string_a.empty_p ()
2154 175816 : && call_string_b.empty_p ()
2155 175816 : && point_a.get_function () != nullptr
2156 2033803 : && point_b.get_function () != nullptr
2157 2083910 : && point_a.get_function () != point_b.get_function ())
2158 : {
2159 50107 : if (int cmp = ka.m_worklist.m_plan.cmp_function (point_a.get_function (),
2160 : point_b.get_function ()))
2161 : return cmp;
2162 : }
2163 :
2164 : /* Sort by callstring, so that nodes with deeper call strings are processed
2165 : before those with shallower call strings.
2166 : If we have
2167 : splitting BB
2168 : / \
2169 : / \
2170 : fn call no fn call
2171 : \ /
2172 : \ /
2173 : join BB
2174 : then we want the path inside the function call to be fully explored up
2175 : to the return to the join BB before we explore on the "no fn call" path,
2176 : so that both enodes at the join BB reach the front of the worklist at
2177 : the same time and thus have a chance of being merged. */
2178 1858367 : int cs_cmp = call_string::cmp (call_string_a, call_string_b);
2179 1858367 : if (cs_cmp)
2180 : return cs_cmp;
2181 :
2182 : /* Order by SCC. */
2183 1676473 : int scc_id_a = ka.get_scc_id (ka.m_enode);
2184 1676473 : int scc_id_b = kb.get_scc_id (kb.m_enode);
2185 1676473 : if (scc_id_a != scc_id_b)
2186 1283883 : return scc_id_a - scc_id_b;
2187 :
2188 : /* If in same SCC, order by supernode index (an arbitrary but stable
2189 : ordering). */
2190 392590 : const supernode *snode_a = ka.m_enode->get_supernode ();
2191 392590 : const supernode *snode_b = kb.m_enode->get_supernode ();
2192 392590 : if (snode_a == nullptr)
2193 : {
2194 0 : if (snode_b != nullptr)
2195 : /* One is nullptr. */
2196 : return -1;
2197 : else
2198 : /* Both are nullptr. */
2199 0 : return 0;
2200 : }
2201 392590 : if (snode_b == nullptr)
2202 : /* One is nullptr. */
2203 : return 1;
2204 : /* Neither are nullptr. */
2205 389177 : gcc_assert (snode_a && snode_b);
2206 389177 : if (snode_a->m_bb->index != snode_b->m_bb->index)
2207 34621 : return snode_a->m_bb->index - snode_b->m_bb->index;
2208 354556 : if (snode_a->m_id != snode_b->m_id)
2209 54540 : return snode_a->m_id - snode_b->m_id;
2210 :
2211 300016 : gcc_assert (snode_a == snode_b);
2212 :
2213 : /* Otherwise, we ought to have the same program_point. */
2214 300016 : gcc_assert (point_a == point_b);
2215 :
2216 : const program_state &state_a = ka.m_enode->get_state ();
2217 : const program_state &state_b = kb.m_enode->get_state ();
2218 :
2219 : /* Sort by sm-state, so that identical sm-states are grouped
2220 : together in the worklist. */
2221 1321000 : for (unsigned sm_idx = 0; sm_idx < state_a.m_checker_states.length ();
2222 : ++sm_idx)
2223 : {
2224 1209480 : sm_state_map *smap_a = state_a.m_checker_states[sm_idx];
2225 1209480 : sm_state_map *smap_b = state_b.m_checker_states[sm_idx];
2226 :
2227 1209480 : if (int smap_cmp = sm_state_map::cmp (*smap_a, *smap_b))
2228 : return smap_cmp;
2229 : }
2230 :
2231 : /* Otherwise, we have two enodes at the same program point but with
2232 : different states. We don't have a good total ordering on states,
2233 : so order them by enode index, so that we have at least have a
2234 : stable sort. */
2235 111520 : return ka.m_enode->m_index - kb.m_enode->m_index;
2236 : }
2237 :
2238 : /* Return a new json::object of the form
2239 : {"scc" : [per-snode-IDs]}, */
2240 :
2241 : std::unique_ptr<json::object>
2242 0 : worklist::to_json () const
2243 : {
2244 0 : auto worklist_obj = std::make_unique<json::object> ();
2245 :
2246 0 : worklist_obj->set ("scc", m_scc.to_json ());
2247 :
2248 : /* The following field isn't yet being JSONified:
2249 : queue_t m_queue; */
2250 :
2251 0 : return worklist_obj;
2252 : }
2253 :
2254 : /* exploded_graph's ctor. */
2255 :
2256 3423 : exploded_graph::exploded_graph (const supergraph &sg, logger *logger,
2257 : const extrinsic_state &ext_state,
2258 : const state_purge_map *purge_map,
2259 : const analysis_plan &plan,
2260 3423 : int verbosity)
2261 3423 : : m_sg (sg), m_logger (logger),
2262 3423 : m_worklist (*this, plan),
2263 3423 : m_ext_state (ext_state),
2264 3423 : m_purge_map (purge_map),
2265 3423 : m_plan (plan),
2266 3423 : m_diagnostic_manager (logger, ext_state.get_engine (), verbosity),
2267 6842 : m_global_stats (m_sg.m_nodes.length ()),
2268 10265 : m_functionless_stats (m_sg.m_nodes.length ())
2269 : {
2270 6846 : m_origin = get_or_create_node
2271 3423 : (program_point::origin (*ext_state.get_model_manager ()),
2272 6846 : program_state (ext_state), nullptr);
2273 3423 : }
2274 :
2275 : /* exploded_graph's dtor. */
2276 :
2277 3423 : exploded_graph::~exploded_graph ()
2278 : {
2279 470241 : for (auto iter : m_per_point_data)
2280 466818 : delete iter.second;
2281 4157 : for (auto iter : m_per_function_data)
2282 367 : delete iter.second;
2283 17253 : for (auto iter : m_per_function_stats)
2284 10411 : delete iter.second;
2285 20531 : for (auto iter : m_per_call_string_data)
2286 8554 : delete iter.second;
2287 6846 : }
2288 :
2289 : /* Subroutine for use when implementing __attribute__((tainted_args))
2290 : on functions and on function pointer fields in structs.
2291 :
2292 : Called on STATE representing a call to FNDECL.
2293 : Mark all params of FNDECL in STATE as "tainted". Mark the value of all
2294 : regions pointed to by params of FNDECL as "tainted".
2295 :
2296 : Return true if successful; return false if the "taint" state machine
2297 : was not found. */
2298 :
2299 : static bool
2300 184 : mark_params_as_tainted (program_state *state, tree fndecl,
2301 : const extrinsic_state &ext_state)
2302 : {
2303 184 : unsigned taint_sm_idx;
2304 184 : if (!ext_state.get_sm_idx_by_name ("taint", &taint_sm_idx))
2305 : return false;
2306 184 : sm_state_map *smap = state->m_checker_states[taint_sm_idx];
2307 :
2308 184 : const state_machine &sm = ext_state.get_sm (taint_sm_idx);
2309 184 : state_machine::state_t tainted = sm.get_state_by_name ("tainted");
2310 :
2311 184 : region_model_manager *mgr = ext_state.get_model_manager ();
2312 :
2313 184 : function *fun = DECL_STRUCT_FUNCTION (fndecl);
2314 184 : gcc_assert (fun);
2315 :
2316 435 : for (tree iter_parm = DECL_ARGUMENTS (fndecl); iter_parm;
2317 251 : iter_parm = DECL_CHAIN (iter_parm))
2318 : {
2319 251 : tree param = iter_parm;
2320 251 : if (tree parm_default_ssa = ssa_default_def (fun, iter_parm))
2321 193 : param = parm_default_ssa;
2322 251 : const region *param_reg = state->m_region_model->get_lvalue (param, nullptr);
2323 251 : const svalue *init_sval = mgr->get_or_create_initial_value (param_reg);
2324 251 : smap->set_state (state->m_region_model, init_sval,
2325 : tainted, nullptr /*origin_new_sval*/, ext_state);
2326 251 : if (POINTER_TYPE_P (TREE_TYPE (param)))
2327 : {
2328 48 : const region *pointee_reg = mgr->get_symbolic_region (init_sval);
2329 : /* Mark "*param" as tainted. */
2330 48 : const svalue *init_pointee_sval
2331 48 : = mgr->get_or_create_initial_value (pointee_reg);
2332 48 : smap->set_state (state->m_region_model, init_pointee_sval,
2333 : tainted, nullptr /*origin_new_sval*/, ext_state);
2334 : }
2335 : }
2336 :
2337 : return true;
2338 : }
2339 :
2340 : /* Custom event for use by tainted_args_function_info when a function
2341 : has been marked with __attribute__((tainted_args)). */
2342 :
2343 : class tainted_args_function_custom_event : public custom_event
2344 : {
2345 : public:
2346 108 : tainted_args_function_custom_event (const event_loc_info &loc_info)
2347 108 : : custom_event (loc_info),
2348 108 : m_fndecl (loc_info.m_fndecl)
2349 : {
2350 : }
2351 :
2352 : void
2353 216 : print_desc (pretty_printer &pp) const final override
2354 : {
2355 216 : pp_printf (&pp,
2356 : "function %qE marked with %<__attribute__((tainted_args))%>",
2357 216 : m_fndecl);
2358 216 : }
2359 :
2360 : private:
2361 : tree m_fndecl;
2362 : };
2363 :
2364 : /* Custom exploded_edge info for top-level calls to a function
2365 : marked with __attribute__((tainted_args)). */
2366 :
2367 : class tainted_args_function_info : public custom_edge_info
2368 : {
2369 : public:
2370 174 : tainted_args_function_info (tree fndecl)
2371 174 : : m_fndecl (fndecl)
2372 : {}
2373 :
2374 0 : void print (pretty_printer *pp) const final override
2375 : {
2376 0 : pp_string (pp, "call to tainted_args function");
2377 0 : };
2378 :
2379 272 : bool update_model (region_model *model,
2380 : const exploded_edge *eedge,
2381 : region_model_context *ctxt) const final override
2382 : {
2383 272 : function *fun = eedge->m_dest->get_function ();
2384 272 : gcc_assert (fun);
2385 272 : model->push_frame (*fun, nullptr, nullptr, ctxt);
2386 272 : return true;
2387 : }
2388 :
2389 108 : void add_events_to_path (checker_path *emission_path,
2390 : const exploded_edge &,
2391 : pending_diagnostic &,
2392 : const state_transition *) const final override
2393 : {
2394 108 : emission_path->add_event
2395 108 : (std::make_unique<tainted_args_function_custom_event>
2396 108 : (event_loc_info (DECL_SOURCE_LOCATION (m_fndecl), m_fndecl, 0)));
2397 108 : }
2398 :
2399 : private:
2400 : tree m_fndecl;
2401 : };
2402 :
2403 : /* Ensure that there is an exploded_node representing an external call to
2404 : FUN, adding it to the worklist if creating it.
2405 :
2406 : Add an edge from the origin exploded_node to the function entrypoint
2407 : exploded_node.
2408 :
2409 : Return the exploded_node for the entrypoint to the function. */
2410 :
2411 : exploded_node *
2412 10559 : exploded_graph::add_function_entry (const function &fun)
2413 : {
2414 10559 : gcc_assert (gimple_has_body_p (fun.decl));
2415 :
2416 : /* Be idempotent. */
2417 10559 : function *key = const_cast<function *> (&fun);
2418 10559 : if (m_functions_with_enodes.contains (key))
2419 : {
2420 294 : logger * const logger = get_logger ();
2421 294 : if (logger)
2422 0 : logger->log ("entrypoint for %qE already exists", fun.decl);
2423 294 : return nullptr;
2424 : }
2425 :
2426 10265 : program_point point
2427 10265 : = program_point::from_function_entry (*m_ext_state.get_model_manager (),
2428 : m_sg, fun);
2429 10265 : program_state state (m_ext_state);
2430 10265 : state.push_frame (m_ext_state, fun);
2431 :
2432 10265 : std::unique_ptr<custom_edge_info> edge_info = nullptr;
2433 :
2434 10265 : if (lookup_attribute ("tainted_args", DECL_ATTRIBUTES (fun.decl)))
2435 : {
2436 174 : if (mark_params_as_tainted (&state, fun.decl, m_ext_state))
2437 174 : edge_info = std::make_unique<tainted_args_function_info> (fun.decl);
2438 : }
2439 :
2440 10265 : if (!state.m_valid)
2441 : return nullptr;
2442 :
2443 10265 : exploded_node *enode = get_or_create_node (point, state, nullptr);
2444 10265 : if (!enode)
2445 : return nullptr;
2446 :
2447 10257 : add_edge (m_origin, enode, nullptr, false, std::move (edge_info));
2448 :
2449 10257 : m_functions_with_enodes.add (key);
2450 :
2451 10257 : return enode;
2452 10265 : }
2453 :
2454 : /* Get or create an exploded_node for (POINT, STATE).
2455 : If a new node is created and ADD_TO_WORKLIST is true,
2456 : it is added to the worklist.
2457 :
2458 : Use ENODE_FOR_DIAG, a pre-existing enode, for any diagnostics
2459 : that need to be emitted (e.g. when purging state *before* we have
2460 : a new enode). */
2461 :
2462 : exploded_node *
2463 400394 : exploded_graph::get_or_create_node (const program_point &point,
2464 : const program_state &state,
2465 : exploded_node *enode_for_diag,
2466 : bool add_to_worklist)
2467 : {
2468 400394 : logger * const logger = get_logger ();
2469 400394 : LOG_FUNC (logger);
2470 400394 : if (logger)
2471 : {
2472 214 : format f (false);
2473 214 : pretty_printer *pp = logger->get_printer ();
2474 214 : logger->start_log_line ();
2475 214 : pp_string (pp, "point: ");
2476 214 : point.print (pp, f);
2477 214 : logger->end_log_line ();
2478 214 : logger->start_log_line ();
2479 214 : pp_string (pp, "state: ");
2480 214 : state.dump_to_pp (m_ext_state, true, false, pp);
2481 214 : logger->end_log_line ();
2482 : }
2483 :
2484 : /* Stop exploring paths for which we don't know how to effectively
2485 : model the state. */
2486 400394 : if (!state.m_valid)
2487 : {
2488 0 : if (logger)
2489 0 : logger->log ("invalid state; not creating node");
2490 0 : return nullptr;
2491 : }
2492 :
2493 400394 : if (point.get_call_string ().calc_recursion_depth ()
2494 400394 : > param_analyzer_max_recursion_depth)
2495 : {
2496 603 : if (logger)
2497 0 : logger->log ("rejecting node: recursion limit exceeded");
2498 603 : return nullptr;
2499 : }
2500 :
2501 796159 : auto_cfun sentinel (point.get_function ());
2502 :
2503 399791 : state.validate (get_ext_state ());
2504 :
2505 : //state.dump (get_ext_state ());
2506 :
2507 : /* Prune state to try to improve the chances of a cache hit,
2508 : avoiding generating redundant nodes. */
2509 399791 : uncertainty_t uncertainty;
2510 399791 : program_state pruned_state
2511 399791 : = state.prune_for_point (*this, point, enode_for_diag, &uncertainty);
2512 :
2513 399791 : pruned_state.validate (get_ext_state ());
2514 :
2515 : //pruned_state.dump (get_ext_state ());
2516 :
2517 399791 : if (logger)
2518 : {
2519 214 : pretty_printer *pp = logger->get_printer ();
2520 214 : logger->start_log_line ();
2521 214 : pp_string (pp, "pruned_state: ");
2522 214 : pruned_state.dump_to_pp (m_ext_state, true, false, pp);
2523 214 : logger->end_log_line ();
2524 214 : pruned_state.m_region_model->dump_to_pp (logger->get_printer (), true,
2525 : false);
2526 : }
2527 :
2528 796159 : stats *per_fn_stats = get_or_create_function_stats (point.get_function ());
2529 :
2530 399791 : stats *per_cs_stats
2531 399791 : = &get_or_create_per_call_string_data (point.get_call_string ())->m_stats;
2532 :
2533 399791 : point_and_state ps (point, pruned_state);
2534 399791 : ps.validate (m_ext_state);
2535 399791 : if (exploded_node **slot = m_point_and_state_to_node.get (&ps))
2536 : {
2537 : /* An exploded_node for PS already exists. */
2538 3338 : if (logger)
2539 11 : logger->log ("reused EN: %i", (*slot)->m_index);
2540 3338 : m_global_stats.m_node_reuse_count++;
2541 3338 : per_fn_stats->m_node_reuse_count++;
2542 3338 : per_cs_stats->m_node_reuse_count++;
2543 3338 : return *slot;
2544 : }
2545 :
2546 396453 : per_program_point_data *per_point_data
2547 396453 : = get_or_create_per_program_point_data (point);
2548 :
2549 : /* Consider merging state with another enode at this program_point. */
2550 933304 : if (flag_analyzer_state_merge && point.state_merge_at_p ())
2551 : {
2552 : exploded_node *existing_enode;
2553 : unsigned i;
2554 378021 : FOR_EACH_VEC_ELT (per_point_data->m_enodes, i, existing_enode)
2555 : {
2556 233812 : if (logger)
2557 130 : logger->log ("considering merging with existing EN: %i for point",
2558 130 : existing_enode->m_index);
2559 233812 : gcc_assert (existing_enode->get_point () == point);
2560 233812 : const program_state &existing_state = existing_enode->get_state ();
2561 :
2562 : /* This merges successfully within the loop. */
2563 :
2564 233812 : program_state merged_state (m_ext_state);
2565 233812 : if (pruned_state.can_merge_with_p (existing_state, m_ext_state, point,
2566 : &merged_state))
2567 : {
2568 27553 : merged_state.validate (m_ext_state);
2569 27553 : if (logger)
2570 63 : logger->log ("merging new state with that of EN: %i",
2571 63 : existing_enode->m_index);
2572 :
2573 : /* Try again for a cache hit.
2574 : Whether we get one or not, merged_state's value_ids have no
2575 : relationship to those of the input state, and thus to those
2576 : of CHANGE, so we must purge any svalue_ids from *CHANGE. */
2577 27553 : ps.set_state (merged_state);
2578 :
2579 27553 : if (exploded_node **slot = m_point_and_state_to_node.get (&ps))
2580 : {
2581 : /* An exploded_node for PS already exists. */
2582 1841 : if (logger)
2583 1 : logger->log ("reused EN: %i", (*slot)->m_index);
2584 1841 : m_global_stats.m_node_reuse_after_merge_count++;
2585 1841 : per_fn_stats->m_node_reuse_after_merge_count++;
2586 1841 : per_cs_stats->m_node_reuse_after_merge_count++;
2587 1841 : return *slot;
2588 : }
2589 : }
2590 : else
2591 206259 : if (logger)
2592 67 : logger->log ("not merging new state with that of EN: %i",
2593 67 : existing_enode->m_index);
2594 233812 : }
2595 : }
2596 :
2597 : /* Impose a limit on the number of enodes per program point, and
2598 : simply stop if we exceed it. */
2599 394612 : if ((int)per_point_data->m_enodes.length ()
2600 394612 : >= param_analyzer_max_enodes_per_program_point)
2601 : {
2602 2198 : pretty_printer pp;
2603 2198 : point.print (&pp, format (false));
2604 2198 : print_enode_indices (&pp, per_point_data->m_enodes);
2605 2198 : if (logger)
2606 0 : logger->log ("not creating enode; too many at program point: %s",
2607 : pp_formatted_text (&pp));
2608 4392 : warning_at (point.get_location (), OPT_Wanalyzer_too_complex,
2609 : "terminating analysis for this program point: %s",
2610 : pp_formatted_text (&pp));
2611 2198 : per_point_data->m_excess_enodes++;
2612 2198 : return nullptr;
2613 2198 : }
2614 :
2615 392414 : ps.validate (m_ext_state);
2616 :
2617 : /* An exploded_node for "ps" doesn't already exist; create one. */
2618 781409 : exploded_node *node = new exploded_node (ps, m_nodes.length ());
2619 392414 : add_node (node);
2620 392414 : m_point_and_state_to_node.put (node->get_ps_key (), node);
2621 :
2622 : /* Update per-program_point data. */
2623 392414 : per_point_data->m_enodes.safe_push (node);
2624 :
2625 392414 : m_global_stats.m_num_nodes++;
2626 392414 : per_fn_stats->m_num_nodes++;
2627 392414 : per_cs_stats->m_num_nodes++;
2628 :
2629 392414 : if (logger)
2630 : {
2631 202 : format f (false);
2632 202 : pretty_printer *pp = logger->get_printer ();
2633 202 : logger->log ("created EN: %i", node->m_index);
2634 202 : logger->start_log_line ();
2635 202 : pp_string (pp, "point: ");
2636 202 : point.print (pp, f);
2637 202 : logger->end_log_line ();
2638 202 : logger->start_log_line ();
2639 202 : pp_string (pp, "state: ");
2640 202 : ps.get_state ().dump_to_pp (m_ext_state, true, false, pp);
2641 202 : logger->end_log_line ();
2642 : }
2643 :
2644 : /* Add the new node to the worlist. */
2645 392414 : if (add_to_worklist)
2646 385220 : m_worklist.add_node (node);
2647 : else
2648 7194 : node->set_status (exploded_node::status::special);
2649 : return node;
2650 800185 : }
2651 :
2652 : /* Add an exploded_edge from SRC to DEST, recording its association
2653 : with SEDGE (which may be NULL), and, if non-NULL, taking ownership
2654 : of CUSTOM_INFO. COULD_DO_WORK is used for detecting infinite loops.
2655 : Return the newly-created eedge. */
2656 :
2657 : exploded_edge *
2658 406557 : exploded_graph::add_edge (exploded_node *src, exploded_node *dest,
2659 : const superedge *sedge, bool could_do_work,
2660 : std::unique_ptr<custom_edge_info> custom_info)
2661 : {
2662 406557 : if (get_logger ())
2663 213 : get_logger ()->log ("creating edge EN: %i -> EN: %i",
2664 213 : src->m_index, dest->m_index);
2665 406557 : exploded_edge *e
2666 : = new exploded_edge (src, dest, sedge, could_do_work,
2667 406557 : std::move (custom_info));
2668 406557 : digraph<eg_traits>::add_edge (e);
2669 406557 : return e;
2670 : }
2671 :
2672 : /* Ensure that this graph has per-program_point-data for POINT;
2673 : borrow a pointer to it. */
2674 :
2675 : per_program_point_data *
2676 396453 : exploded_graph::
2677 : get_or_create_per_program_point_data (const program_point &point)
2678 : {
2679 396453 : if (per_program_point_data **slot = m_per_point_data.get (&point))
2680 163044 : return *slot;
2681 :
2682 233409 : per_program_point_data *per_point_data = new per_program_point_data (point);
2683 233409 : m_per_point_data.put (&per_point_data->m_key, per_point_data);
2684 233409 : return per_point_data;
2685 : }
2686 :
2687 : /* Get this graph's per-program-point-data for POINT if there is any,
2688 : otherwise nullptr. */
2689 :
2690 : per_program_point_data *
2691 0 : exploded_graph::get_per_program_point_data (const program_point &point) const
2692 : {
2693 0 : if (per_program_point_data **slot
2694 0 : = const_cast <point_map_t &> (m_per_point_data).get (&point))
2695 0 : return *slot;
2696 :
2697 : return nullptr;
2698 : }
2699 :
2700 : /* Ensure that this graph has per-call_string-data for CS;
2701 : borrow a pointer to it. */
2702 :
2703 : per_call_string_data *
2704 399791 : exploded_graph::get_or_create_per_call_string_data (const call_string &cs)
2705 : {
2706 399791 : if (per_call_string_data **slot = m_per_call_string_data.get (&cs))
2707 391237 : return *slot;
2708 :
2709 17104 : per_call_string_data *data = new per_call_string_data (cs, m_sg.m_nodes.length ());
2710 8554 : m_per_call_string_data.put (&data->m_key,
2711 : data);
2712 8554 : return data;
2713 : }
2714 :
2715 : /* Ensure that this graph has per-function-data for FUN;
2716 : borrow a pointer to it. */
2717 :
2718 : per_function_data *
2719 629 : exploded_graph::get_or_create_per_function_data (function *fun)
2720 : {
2721 629 : if (per_function_data **slot = m_per_function_data.get (fun))
2722 262 : return *slot;
2723 :
2724 367 : per_function_data *data = new per_function_data ();
2725 367 : m_per_function_data.put (fun, data);
2726 367 : return data;
2727 : }
2728 :
2729 : /* Get this graph's per-function-data for FUN if there is any,
2730 : otherwise nullptr. */
2731 :
2732 : per_function_data *
2733 787 : exploded_graph::get_per_function_data (function *fun) const
2734 : {
2735 1574 : if (per_function_data **slot
2736 787 : = const_cast <per_function_data_t &> (m_per_function_data).get (fun))
2737 749 : return *slot;
2738 :
2739 : return nullptr;
2740 : }
2741 :
2742 : /* Return true if FUN should be traversed directly, rather than only as
2743 : called via other functions. */
2744 :
2745 : static bool
2746 10415 : toplevel_function_p (const function &fun, logger *logger)
2747 : {
2748 : /* Don't directly traverse into functions that have an "__analyzer_"
2749 : prefix. Doing so is useful for the analyzer test suite, allowing
2750 : us to have functions that are called in traversals, but not directly
2751 : explored, thus testing how the analyzer handles calls and returns.
2752 : With this, we can have DejaGnu directives that cover just the case
2753 : of where a function is called by another function, without generating
2754 : excess messages from the case of the first function being traversed
2755 : directly. */
2756 : #define ANALYZER_PREFIX "__analyzer_"
2757 10415 : if (!strncmp (IDENTIFIER_POINTER (DECL_NAME (fun.decl)), ANALYZER_PREFIX,
2758 : strlen (ANALYZER_PREFIX)))
2759 : {
2760 150 : if (logger)
2761 0 : logger->log ("not traversing %qE (starts with %qs)",
2762 : fun.decl, ANALYZER_PREFIX);
2763 150 : return false;
2764 : }
2765 :
2766 10265 : if (logger)
2767 6 : logger->log ("traversing %qE (all checks passed)", fun.decl);
2768 :
2769 : return true;
2770 : }
2771 :
2772 : /* Custom event for use by tainted_call_info when a callback field has been
2773 : marked with __attribute__((tainted_args)), for labelling the field. */
2774 :
2775 : class tainted_args_field_custom_event : public custom_event
2776 : {
2777 : public:
2778 4 : tainted_args_field_custom_event (tree field)
2779 4 : : custom_event (event_loc_info (DECL_SOURCE_LOCATION (field), NULL_TREE, 0)),
2780 4 : m_field (field)
2781 : {
2782 4 : }
2783 :
2784 8 : void print_desc (pretty_printer &pp) const final override
2785 : {
2786 8 : pp_printf (&pp,
2787 : "field %qE of %qT"
2788 : " is marked with %<__attribute__((tainted_args))%>",
2789 8 : m_field, DECL_CONTEXT (m_field));
2790 8 : }
2791 :
2792 : private:
2793 : tree m_field;
2794 : };
2795 :
2796 : /* Custom event for use by tainted_call_info when a callback field has been
2797 : marked with __attribute__((tainted_args)), for labelling the function used
2798 : in that callback. */
2799 :
2800 : class tainted_args_callback_custom_event : public custom_event
2801 : {
2802 : public:
2803 4 : tainted_args_callback_custom_event (const event_loc_info &loc_info,
2804 : tree field)
2805 4 : : custom_event (loc_info),
2806 4 : m_field (field)
2807 : {
2808 : }
2809 :
2810 8 : void print_desc (pretty_printer &pp) const final override
2811 : {
2812 8 : pp_printf (&pp,
2813 : "function %qE used as initializer for field %qE"
2814 : " marked with %<__attribute__((tainted_args))%>",
2815 8 : get_fndecl (), m_field);
2816 8 : }
2817 :
2818 : private:
2819 : tree m_field;
2820 : };
2821 :
2822 : /* Custom edge info for use when adding a function used by a callback field
2823 : marked with '__attribute__((tainted_args))'. */
2824 :
2825 : class tainted_args_call_info : public custom_edge_info
2826 : {
2827 : public:
2828 10 : tainted_args_call_info (tree field, tree fndecl, location_t loc)
2829 10 : : m_field (field), m_fndecl (fndecl), m_loc (loc)
2830 : {}
2831 :
2832 0 : void print (pretty_printer *pp) const final override
2833 : {
2834 0 : pp_string (pp, "call to tainted field");
2835 0 : };
2836 :
2837 13 : bool update_model (region_model *model,
2838 : const exploded_edge *eedge,
2839 : region_model_context *) const final override
2840 : {
2841 26 : model->push_frame (*eedge->m_dest->get_function (),
2842 : nullptr, nullptr, nullptr);
2843 13 : return true;
2844 : }
2845 :
2846 4 : void add_events_to_path (checker_path *emission_path,
2847 : const exploded_edge &,
2848 : pending_diagnostic &,
2849 : const state_transition *) const final override
2850 : {
2851 : /* Show the field in the struct declaration, e.g.
2852 : "(1) field 'store' is marked with '__attribute__((tainted_args))'" */
2853 4 : emission_path->add_event
2854 4 : (std::make_unique<tainted_args_field_custom_event> (m_field));
2855 :
2856 : /* Show the callback in the initializer
2857 : e.g.
2858 : "(2) function 'gadget_dev_desc_UDC_store' used as initializer
2859 : for field 'store' marked with '__attribute__((tainted_args))'". */
2860 4 : emission_path->add_event
2861 4 : (std::make_unique<tainted_args_callback_custom_event>
2862 4 : (event_loc_info (m_loc, m_fndecl, 0),
2863 : m_field));
2864 4 : }
2865 :
2866 : private:
2867 : tree m_field;
2868 : tree m_fndecl;
2869 : location_t m_loc;
2870 : };
2871 :
2872 : /* Given an initializer at LOC for FIELD marked with
2873 : '__attribute__((tainted_args))' initialized with FNDECL, add an
2874 : entrypoint to FNDECL to EG (and to its worklist) where the params to
2875 : FNDECL are marked as tainted. */
2876 :
2877 : static void
2878 10 : add_tainted_args_callback (exploded_graph *eg, tree field, tree fndecl,
2879 : location_t loc)
2880 : {
2881 10 : logger *logger = eg->get_logger ();
2882 :
2883 10 : LOG_SCOPE (logger);
2884 :
2885 10 : if (!gimple_has_body_p (fndecl))
2886 : return;
2887 :
2888 10 : const extrinsic_state &ext_state = eg->get_ext_state ();
2889 :
2890 10 : function *fun = DECL_STRUCT_FUNCTION (fndecl);
2891 10 : gcc_assert (fun);
2892 :
2893 10 : program_point point
2894 10 : = program_point::from_function_entry (*ext_state.get_model_manager (),
2895 : eg->get_supergraph (), *fun);
2896 10 : program_state state (ext_state);
2897 10 : state.push_frame (ext_state, *fun);
2898 :
2899 10 : if (!mark_params_as_tainted (&state, fndecl, ext_state))
2900 : return;
2901 :
2902 10 : if (!state.m_valid)
2903 : return;
2904 :
2905 10 : exploded_node *enode = eg->get_or_create_node (point, state, nullptr);
2906 10 : if (logger)
2907 : {
2908 0 : if (enode)
2909 0 : logger->log ("created EN %i for tainted_args %qE entrypoint",
2910 0 : enode->m_index, fndecl);
2911 : else
2912 : {
2913 0 : logger->log ("did not create enode for tainted_args %qE entrypoint",
2914 : fndecl);
2915 0 : return;
2916 : }
2917 : }
2918 :
2919 10 : eg->add_edge (eg->get_origin (), enode, nullptr, false,
2920 10 : std::make_unique<tainted_args_call_info> (field, fndecl, loc));
2921 10 : }
2922 :
2923 : /* Callback for walk_tree for finding callbacks within initializers;
2924 : ensure that any callback initializer where the corresponding field is
2925 : marked with '__attribute__((tainted_args))' is treated as an entrypoint
2926 : to the analysis, special-casing that the inputs to the callback are
2927 : untrustworthy. */
2928 :
2929 : static tree
2930 27625 : add_any_callbacks (tree *tp, int *, void *data)
2931 : {
2932 27625 : exploded_graph *eg = (exploded_graph *)data;
2933 27625 : if (TREE_CODE (*tp) == CONSTRUCTOR)
2934 : {
2935 : /* Find fields with the "tainted_args" attribute.
2936 : walk_tree only walks the values, not the index values;
2937 : look at the index values. */
2938 : unsigned HOST_WIDE_INT idx;
2939 : constructor_elt *ce;
2940 :
2941 18484 : for (idx = 0; vec_safe_iterate (CONSTRUCTOR_ELTS (*tp), idx, &ce);
2942 : idx++)
2943 13732 : if (ce->index && TREE_CODE (ce->index) == FIELD_DECL)
2944 12774 : if (lookup_attribute ("tainted_args", DECL_ATTRIBUTES (ce->index)))
2945 : {
2946 10 : tree value = ce->value;
2947 10 : if (TREE_CODE (value) == ADDR_EXPR
2948 10 : && TREE_CODE (TREE_OPERAND (value, 0)) == FUNCTION_DECL)
2949 20 : add_tainted_args_callback (eg, ce->index,
2950 10 : TREE_OPERAND (value, 0),
2951 10 : EXPR_LOCATION (value));
2952 : }
2953 : }
2954 :
2955 27625 : return NULL_TREE;
2956 : }
2957 :
2958 : /* Add initial nodes to EG, with entrypoints for externally-callable
2959 : functions. */
2960 :
2961 : void
2962 3423 : exploded_graph::build_initial_worklist ()
2963 : {
2964 3423 : logger * const logger = get_logger ();
2965 3423 : LOG_SCOPE (logger);
2966 :
2967 3423 : cgraph_node *node;
2968 13838 : FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
2969 : {
2970 10415 : function *fun = node->get_fun ();
2971 10415 : gcc_assert (fun);
2972 10415 : if (!toplevel_function_p (*fun, logger))
2973 150 : continue;
2974 10265 : exploded_node *enode = add_function_entry (*fun);
2975 10265 : if (logger)
2976 : {
2977 6 : if (enode)
2978 6 : logger->log ("created EN %i for %qE entrypoint",
2979 6 : enode->m_index, fun->decl);
2980 : else
2981 0 : logger->log ("did not create enode for %qE entrypoint", fun->decl);
2982 : }
2983 : }
2984 :
2985 : /* Find callbacks that are reachable from global initializers. */
2986 3423 : varpool_node *vpnode;
2987 9467 : FOR_EACH_VARIABLE (vpnode)
2988 : {
2989 6044 : tree decl = vpnode->decl;
2990 6044 : tree init = DECL_INITIAL (decl);
2991 6044 : if (!init)
2992 1273 : continue;
2993 4771 : walk_tree (&init, add_any_callbacks, this, nullptr);
2994 : }
2995 3423 : }
2996 :
2997 : /* The main loop of the analysis.
2998 : Take freshly-created exploded_nodes from the worklist, calling
2999 : process_node on them to explore the <point, state> graph.
3000 : Add edges to their successors, potentially creating new successors
3001 : (which are also added to the worklist). */
3002 :
3003 : void
3004 3423 : exploded_graph::process_worklist ()
3005 : {
3006 3423 : logger * const logger = get_logger ();
3007 3423 : LOG_SCOPE (logger);
3008 3423 : auto_timevar tv (TV_ANALYZER_WORKLIST);
3009 :
3010 370951 : while (m_worklist.length () > 0)
3011 : {
3012 364182 : exploded_node *node = m_worklist.take_next ();
3013 364182 : gcc_assert (node->get_status () == exploded_node::status::worklist);
3014 :
3015 364182 : if (logger)
3016 194 : logger->log ("next to process: EN: %i", node->m_index);
3017 :
3018 : /* If we have a run of nodes at the same point, try merging and
3019 : processing them together, rather than pairwise or individually. */
3020 364182 : if (flag_analyzer_state_merge
3021 364182 : && node->get_point ().state_merge_at_p ())
3022 123569 : if (maybe_process_run_of_enodes (node))
3023 7189 : goto handle_limit;
3024 :
3025 : /* Avoid exponential explosions of nodes by attempting to merge
3026 : nodes that are at the same program point and which have
3027 : sufficiently similar state. */
3028 356993 : if (flag_analyzer_state_merge && node != m_origin)
3029 349966 : if (exploded_node *node_2 = m_worklist.peek_next ())
3030 : {
3031 299343 : gcc_assert (node_2->get_status ()
3032 : == exploded_node::status::worklist);
3033 299343 : gcc_assert (node != node_2);
3034 :
3035 299343 : if (logger)
3036 148 : logger->log ("peek worklist: EN: %i", node_2->m_index);
3037 :
3038 299343 : if (node->get_point () == node_2->get_point ())
3039 : {
3040 64317 : const program_point &point = node->get_point ();
3041 64317 : if (logger)
3042 : {
3043 29 : format f (false);
3044 29 : pretty_printer *pp = logger->get_printer ();
3045 29 : logger->start_log_line ();
3046 29 : logger->log_partial
3047 29 : ("got potential merge EN: %i and EN: %i at ",
3048 29 : node->m_index, node_2->m_index);
3049 29 : point.print (pp, f);
3050 29 : logger->end_log_line ();
3051 : }
3052 64317 : const program_state &state = node->get_state ();
3053 64317 : const program_state &state_2 = node_2->get_state ();
3054 :
3055 : /* They shouldn't be equal, or we wouldn't have two
3056 : separate nodes. */
3057 64317 : gcc_assert (state != state_2);
3058 :
3059 64317 : program_state merged_state (m_ext_state);
3060 64317 : if (state.can_merge_with_p (state_2, m_ext_state,
3061 : point, &merged_state))
3062 : {
3063 2676 : if (logger)
3064 4 : logger->log ("merging EN: %i and EN: %i",
3065 4 : node->m_index, node_2->m_index);
3066 :
3067 2676 : if (merged_state == state)
3068 : {
3069 : /* Then merge node_2 into node by adding an edge. */
3070 447 : add_edge (node_2, node, nullptr, false);
3071 :
3072 : /* Remove node_2 from the worklist. */
3073 447 : m_worklist.take_next ();
3074 447 : node_2->set_status (exploded_node::status::merger);
3075 :
3076 : /* Continue processing "node" below. */
3077 : }
3078 2229 : else if (merged_state == state_2)
3079 : {
3080 : /* Then merge node into node_2, and leave node_2
3081 : in the worklist, to be processed on the next
3082 : iteration. */
3083 1302 : add_edge (node, node_2, nullptr, false);
3084 1302 : node->set_status (exploded_node::status::merger);
3085 1302 : continue;
3086 : }
3087 : else
3088 : {
3089 : /* We have a merged state that differs from
3090 : both state and state_2. */
3091 :
3092 : /* Remove node_2 from the worklist. */
3093 927 : m_worklist.take_next ();
3094 :
3095 : /* Create (or get) an exploded node for the merged
3096 : states, adding to the worklist. */
3097 927 : exploded_node *merged_enode
3098 927 : = get_or_create_node (node->get_point (),
3099 : merged_state, node);
3100 927 : if (merged_enode == nullptr)
3101 87 : continue;
3102 :
3103 840 : if (logger)
3104 3 : logger->log ("merged EN: %i and EN: %i into EN: %i",
3105 3 : node->m_index, node_2->m_index,
3106 3 : merged_enode->m_index);
3107 :
3108 : /* "node" and "node_2" have both now been removed
3109 : from the worklist; we should not process them.
3110 :
3111 : "merged_enode" may be a new node; if so it will be
3112 : processed in a subsequent iteration.
3113 : Alternatively, "merged_enode" could be an existing
3114 : node; one way the latter can
3115 : happen is if we end up merging a succession of
3116 : similar nodes into one. */
3117 :
3118 : /* If merged_node is one of the two we were merging,
3119 : add it back to the worklist to ensure it gets
3120 : processed.
3121 :
3122 : Add edges from the merged nodes to it (but not a
3123 : self-edge). */
3124 840 : if (merged_enode == node)
3125 0 : m_worklist.add_node (merged_enode);
3126 : else
3127 : {
3128 840 : add_edge (node, merged_enode, nullptr, false);
3129 840 : node->set_status (exploded_node::status::merger);
3130 : }
3131 :
3132 840 : if (merged_enode == node_2)
3133 6 : m_worklist.add_node (merged_enode);
3134 : else
3135 : {
3136 834 : add_edge (node_2, merged_enode, nullptr, false);
3137 834 : node_2->set_status (exploded_node::status::merger);
3138 : }
3139 :
3140 840 : continue;
3141 840 : }
3142 : }
3143 :
3144 : /* TODO: should we attempt more than two nodes,
3145 : or just do pairs of nodes? (and hope that we get
3146 : a cascade of mergers). */
3147 64317 : }
3148 : }
3149 :
3150 354764 : process_node (node);
3151 :
3152 361953 : handle_limit:
3153 : /* Impose a hard limit on the number of exploded nodes, to ensure
3154 : that the analysis terminates in the face of pathological state
3155 : explosion (or bugs). */
3156 361953 : const int limit
3157 : = (// Per-supernode limit:
3158 361953 : (m_sg.num_nodes () * param_analyzer_bb_explosion_factor)
3159 : // Allow one for the "origin" enode:
3160 361953 : + 1);
3161 361953 : if (m_global_stats.m_num_nodes > limit)
3162 : {
3163 77 : if (logger)
3164 0 : logger->log ("bailing out; too many nodes");
3165 231 : warning_at (node->get_point ().get_location (),
3166 77 : OPT_Wanalyzer_too_complex,
3167 : "analysis bailed out early"
3168 : " (%i enodes)",
3169 : m_nodes.length ());
3170 77 : return;
3171 : }
3172 : }
3173 3423 : }
3174 :
3175 : /* Attempt to process a consecutive run of sufficiently-similar nodes in
3176 : the worklist at a point flagged with state_merge_at_p (having already
3177 : popped ENODE from the head of the worklist).
3178 :
3179 : If we have a consecutive run of enodes in the worklist all of which have
3180 : a single out-edge where all these out-edges are supports_bulk_merge_p and
3181 : all have the same successor snode and call string, then
3182 : process them all together, setting their status to status::bulk_merged,
3183 : and return true.
3184 : Otherwise, return false, in which case ENODE must be processed in the
3185 : normal way.
3186 :
3187 : When processing them all together, generate successor states based
3188 : on the edge op update_state_for_bulk_merger, and then attempt to merge
3189 : these states into a minimal set of merged successor states, partitioning
3190 : the inputs by merged successor state.
3191 :
3192 : Create new exploded nodes for all of the merged states, and add edges
3193 : connecting the input enodes to the corresponding merger exploded nodes.
3194 :
3195 : We hope we have a much smaller number of merged successor states
3196 : compared to the number of input enodes - ideally just one,
3197 : if all successor states can be merged.
3198 :
3199 : Processing and merging many together as one operation rather than as
3200 : pairs avoids scaling issues where per-pair mergers could bloat the
3201 : graph with merger nodes (especially so after switch statements). */
3202 :
3203 : bool
3204 123569 : exploded_graph::
3205 : maybe_process_run_of_enodes (exploded_node *enode)
3206 : {
3207 : /* A struct for tracking per-input state. */
3208 25818 : struct item
3209 : {
3210 25818 : item (exploded_node *input_enode)
3211 25818 : : m_input_enode (input_enode),
3212 25818 : m_processed_state (input_enode->get_state ()),
3213 25818 : m_merger_idx (-1)
3214 : {}
3215 :
3216 : exploded_node *m_input_enode;
3217 : program_state m_processed_state;
3218 : int m_merger_idx;
3219 : };
3220 :
3221 123569 : gcc_assert (enode->get_status () == exploded_node::status::worklist);
3222 :
3223 123569 : const program_point &src_point = enode->get_point ();
3224 123569 : const supernode *src_snode = src_point.get_supernode ();
3225 :
3226 123569 : logger * const logger = get_logger ();
3227 123569 : LOG_SCOPE (logger);
3228 :
3229 186447 : if (src_snode->m_succs.length () != 1)
3230 : return false;
3231 :
3232 99076 : auto sedge = src_snode->m_succs[0];
3233 :
3234 99076 : if (!sedge->supports_bulk_merge_p ())
3235 : return false;
3236 :
3237 36198 : const supernode *dst_snode = src_snode->m_succs[0]->m_dest;
3238 :
3239 : /* Find a run of enodes in the worklist that all have single out-sedges
3240 : go to the same supernode, all of which are bulk-mergeable (i.e. have
3241 : a simple single intraprocedural outcome). */
3242 36198 : auto_vec <exploded_node *> enodes;
3243 36198 : enodes.safe_push (enode);
3244 54827 : while (exploded_node *enode_2 = m_worklist.peek_next ())
3245 : {
3246 48908 : gcc_assert (enode_2->get_status ()
3247 : == exploded_node::status::worklist);
3248 48908 : gcc_assert (enode_2->m_succs.length () == 0);
3249 :
3250 48908 : const program_point &point_2 = enode_2->get_point ();
3251 48908 : const supernode *src_snode_2 = point_2.get_supernode ();
3252 :
3253 48908 : if (src_snode_2->m_succs.length () != 1)
3254 : break;
3255 47972 : auto sedge_2 = src_snode_2->m_succs[0];
3256 47972 : if (sedge_2->m_dest != dst_snode)
3257 : break;
3258 19781 : if (&point_2.get_call_string () != &src_point.get_call_string ())
3259 : break;
3260 18629 : if (!sedge_2->supports_bulk_merge_p ())
3261 : break;
3262 :
3263 18629 : enodes.safe_push (enode_2);
3264 18629 : m_worklist.take_next ();
3265 18629 : }
3266 :
3267 : /* If the only node is ENODE, then give up. */
3268 36198 : if (enodes.length () == 1)
3269 : return false;
3270 :
3271 7189 : if (logger)
3272 4 : logger->log ("got run of %i bulk-mergable enodes going to SN: %i",
3273 4 : enodes.length (), dst_snode->m_id);
3274 :
3275 : /* All of these enodes have a shared intraprocedural successor point
3276 : (even if they were for different in-edges). */
3277 7189 : program_point next_point (sedge->m_dest,
3278 7189 : src_point.get_call_string ());
3279 :
3280 : /* Calculate the successor state for each enode in enodes. */
3281 14378 : auto_delete_vec<item> items (enodes.length ());
3282 7189 : unsigned i;
3283 7189 : exploded_node *iter_enode;
3284 33007 : FOR_EACH_VEC_ELT (enodes, i, iter_enode)
3285 : {
3286 25818 : item *it = new item (iter_enode);
3287 25818 : items.quick_push (it);
3288 25818 : const program_state &state = iter_enode->get_state ();
3289 25818 : program_state *next_state = &it->m_processed_state;
3290 25818 : next_state->validate (m_ext_state);
3291 25818 : gcc_assert (iter_enode->get_supernode ()->m_succs.length () == 1);
3292 25818 : const superedge *iter_sedge = iter_enode->get_supernode ()->m_succs[0];
3293 25818 : if (auto op = iter_sedge->get_op ())
3294 414 : op->update_state_for_bulk_merger (state, *next_state);
3295 25818 : next_state->validate (m_ext_state);
3296 : }
3297 :
3298 : /* Attempt to partition the items into a set of merged states.
3299 : We hope we have a much smaller number of merged states
3300 : compared to the number of input enodes - ideally just one,
3301 : if all can be merged. */
3302 7189 : auto_delete_vec <program_state> merged_states;
3303 7189 : auto_vec<item *> first_item_for_each_merged_state;
3304 7189 : item *it;
3305 33007 : FOR_EACH_VEC_ELT (items, i, it)
3306 : {
3307 25818 : const program_state &it_state = it->m_processed_state;
3308 25818 : program_state *merged_state;
3309 25818 : unsigned iter_merger_idx;
3310 64470 : FOR_EACH_VEC_ELT (merged_states, iter_merger_idx, merged_state)
3311 : {
3312 48485 : merged_state->validate (m_ext_state);
3313 48485 : program_state merge (m_ext_state);
3314 48485 : if (it_state.can_merge_with_p (*merged_state, m_ext_state,
3315 : next_point, &merge))
3316 : {
3317 9833 : *merged_state = merge;
3318 9833 : merged_state->validate (m_ext_state);
3319 9833 : it->m_merger_idx = iter_merger_idx;
3320 9833 : if (logger)
3321 1 : logger->log ("reusing merger state %i for item %i (EN: %i)",
3322 1 : it->m_merger_idx, i, it->m_input_enode->m_index);
3323 9833 : goto got_merger;
3324 : }
3325 48485 : }
3326 : /* If it couldn't be merged with any existing merged_states,
3327 : create a new one. */
3328 15985 : if (it->m_merger_idx == -1)
3329 : {
3330 15985 : it->m_merger_idx = merged_states.length ();
3331 15985 : merged_states.safe_push (new program_state (it_state));
3332 15985 : first_item_for_each_merged_state.safe_push (it);
3333 15985 : if (logger)
3334 8 : logger->log ("using new merger state %i for item %i (EN: %i)",
3335 8 : it->m_merger_idx, i, it->m_input_enode->m_index);
3336 : }
3337 0 : got_merger:
3338 25818 : gcc_assert (it->m_merger_idx >= 0);
3339 25818 : gcc_assert ((unsigned)it->m_merger_idx < merged_states.length ());
3340 : }
3341 :
3342 : /* Create merger nodes. */
3343 21567 : auto_vec<exploded_node *> next_enodes (merged_states.length ());
3344 7189 : program_state *merged_state;
3345 23174 : FOR_EACH_VEC_ELT (merged_states, i, merged_state)
3346 : {
3347 15985 : exploded_node *src_enode
3348 15985 : = first_item_for_each_merged_state[i]->m_input_enode;
3349 15985 : exploded_node *next
3350 15985 : = get_or_create_node (next_point, *merged_state, src_enode);
3351 : /* "next" could be nullptr; we handle that when adding the edges below. */
3352 15985 : next_enodes.quick_push (next);
3353 15985 : if (logger)
3354 : {
3355 8 : if (next)
3356 8 : logger->log ("using EN: %i for merger state %i", next->m_index, i);
3357 : else
3358 0 : logger->log ("using NULL enode for merger state %i", i);
3359 : }
3360 : }
3361 :
3362 : /* Create edges from each input enode to the appropriate successor enode.
3363 : Update the status of the now-processed input enodes. */
3364 33007 : FOR_EACH_VEC_ELT (items, i, it)
3365 : {
3366 25818 : exploded_node *next = next_enodes[it->m_merger_idx];
3367 25818 : if (next)
3368 : {
3369 25248 : gcc_assert (it->m_input_enode->get_supernode ()->m_succs.length ()
3370 : == 1);
3371 25248 : const superedge *sedge
3372 25248 : = it->m_input_enode->get_supernode ()->m_succs[0];
3373 25248 : add_edge (it->m_input_enode, next, sedge,
3374 : false); /* no "work" is done during merger. */
3375 : }
3376 25818 : it->m_input_enode->set_status (exploded_node::status::bulk_merged);
3377 : }
3378 :
3379 7189 : if (logger)
3380 8 : logger->log ("merged %i in-enodes into %i out-enode(s) at SN: %i",
3381 4 : items.length (), merged_states.length (), dst_snode->m_id);
3382 :
3383 7189 : return true;
3384 130758 : }
3385 :
3386 : /* The core of exploded_graph::process_worklist (the main analysis loop),
3387 : handling one node in the worklist.
3388 :
3389 : Get successor <point, state> pairs for NODE, calling get_or_create on
3390 : them, and adding an exploded_edge to each successors.
3391 :
3392 : Freshly-created nodes will be added to the worklist. */
3393 :
3394 : void
3395 354764 : exploded_graph::process_node (exploded_node *node)
3396 : {
3397 354764 : logger * const logger = get_logger ();
3398 354764 : LOG_FUNC_1 (logger, "EN: %i", node->m_index);
3399 :
3400 354764 : node->set_status (exploded_node::status::processed);
3401 :
3402 354764 : const program_point &point = node->get_point ();
3403 :
3404 : /* Update cfun and input_location in case of an ICE: make it easier to
3405 : track down which source construct we're failing to handle. */
3406 706109 : auto_cfun sentinel (node->get_function ());
3407 :
3408 354764 : input_location = node->get_location ();
3409 :
3410 354764 : const program_state &state = node->get_state ();
3411 354764 : if (logger)
3412 : {
3413 187 : pretty_printer *pp = logger->get_printer ();
3414 187 : logger->start_log_line ();
3415 187 : pp_string (pp, "point: ");
3416 187 : point.print (pp, format (false));
3417 187 : pp_string (pp, ", state: ");
3418 187 : state.dump_to_pp (m_ext_state, true, false, pp);
3419 187 : logger->end_log_line ();
3420 : }
3421 :
3422 : /* Don't do anything for the origin enode; the initial population of the
3423 : worklist has already added successor enodes. */
3424 354764 : if (point.get_supernode () == nullptr)
3425 : return;
3426 :
3427 : /* Specialcase for EXIT BBs, which don't have out-edges. */
3428 351345 : if (point.get_supernode ()->exit_p ())
3429 : {
3430 17269 : gcc_assert (point.get_supernode ()->m_succs.length () == 0);
3431 :
3432 17269 : if (point.get_stack_depth () > 1)
3433 : {
3434 : /* Interprocedural return. */
3435 5816 : auto &src_call_string = point.get_call_string ();
3436 :
3437 5816 : const call_string::element_t &top_of_stack
3438 5816 : = src_call_string.get_top_of_stack ();
3439 5816 : const call_string *dst_call_string = src_call_string.get_parent ();
3440 5816 : const program_point dst_point
3441 : (top_of_stack.get_return_snode_in_caller (),
3442 5816 : *dst_call_string);
3443 5816 : auto edge_info
3444 5816 : = std::make_unique<interprocedural_return> (top_of_stack.get_call_stmt ());
3445 :
3446 5816 : const program_state &src_state (node->get_state ());
3447 5816 : program_state dst_state (src_state);
3448 5816 : uncertainty_t uncertainty;
3449 5816 : impl_region_model_context ctxt (*this, node,
3450 : &src_state, &dst_state, &uncertainty,
3451 : nullptr,
3452 5816 : nullptr);
3453 5816 : edge_info->update_state (&dst_state, nullptr, &ctxt);
3454 :
3455 5816 : program_state::detect_leaks (src_state, dst_state,
3456 : nullptr, get_ext_state (),
3457 : &ctxt);
3458 :
3459 11632 : if (exploded_node *next
3460 5816 : = get_or_create_node (dst_point, dst_state, node))
3461 5704 : add_edge (node, next, nullptr, false,
3462 5704 : std::move (edge_info));
3463 11632 : }
3464 : else
3465 : {
3466 : /* End of top-level of analysis for this function.
3467 : Detect leaks, and potentially create a function summary. */
3468 11453 : node->detect_leaks (*this);
3469 :
3470 11453 : if (flag_analyzer_call_summaries
3471 11453 : && point.get_call_string ().empty_p ())
3472 : {
3473 : /* TODO: create function summary
3474 : There can be more than one; each corresponds to a different
3475 : final enode in the function. */
3476 629 : if (logger)
3477 : {
3478 0 : pretty_printer *pp = logger->get_printer ();
3479 0 : logger->start_log_line ();
3480 0 : logger->log_partial
3481 0 : ("would create function summary for %qE; state: ",
3482 : point.get_fndecl ());
3483 0 : state.dump_to_pp (m_ext_state, true, false, pp);
3484 0 : logger->end_log_line ();
3485 : }
3486 629 : per_function_data *per_fn_data
3487 629 : = get_or_create_per_function_data (point.get_function ());
3488 629 : per_fn_data->add_call_summary (node);
3489 : }
3490 : }
3491 :
3492 17269 : return;
3493 : }
3494 :
3495 : /* Traverse into successors of the supernode. */
3496 : int i;
3497 : superedge *succ;
3498 1030001 : FOR_EACH_VEC_ELT (point.get_supernode ()->m_succs, i, succ)
3499 : {
3500 362691 : if (logger)
3501 : {
3502 194 : label_text succ_desc (succ->get_description (false));
3503 194 : logger->log ("considering SN: %i -> SN: %i (%s)",
3504 194 : succ->m_src->m_id, succ->m_dest->m_id,
3505 : succ_desc.get ());
3506 194 : }
3507 :
3508 362691 : program_point next_point (succ->m_dest, point.get_call_string ());
3509 362691 : program_state next_state (state);
3510 362691 : uncertainty_t uncertainty;
3511 :
3512 : /* Find the outcome(s) of any operation on the edge. */
3513 362691 : operation_context op_ctxt (*this, *node, *succ);
3514 :
3515 : /* Skip EH edges. */
3516 362691 : if (auto cfg_edge = succ->get_any_cfg_edge ())
3517 116011 : if (cfg_edge->flags & EDGE_EH)
3518 943 : continue;
3519 :
3520 361748 : if (auto op = succ->get_op ())
3521 306093 : op->execute (op_ctxt);
3522 : else
3523 : {
3524 : /* No-op.
3525 : Unconditional goto to the dst point, which
3526 : must be in same function.
3527 : The supernode changes, but the callstring and
3528 : state do not change. */
3529 55655 : if (logger)
3530 34 : logger->log ("handling no-op edge");
3531 55655 : auto dst_point (op_ctxt.get_next_intraprocedural_point ());
3532 55655 : if (exploded_node *next
3533 55655 : = get_or_create_node (dst_point,
3534 : node->get_state (),
3535 : node))
3536 55486 : add_edge (node, next, succ, false);
3537 : }
3538 362691 : }
3539 354764 : }
3540 :
3541 : /* Ensure that this graph has a stats instance for FN, return it.
3542 : FN can be nullptr, in which case a stats instances is returned covering
3543 : "functionless" parts of the graph (the origin node). */
3544 :
3545 : stats *
3546 399791 : exploded_graph::get_or_create_function_stats (function *fn)
3547 : {
3548 399791 : if (!fn)
3549 3423 : return &m_functionless_stats;
3550 :
3551 396368 : if (stats **slot = m_per_function_stats.get (fn))
3552 385957 : return *slot;
3553 : else
3554 : {
3555 10411 : int num_supernodes = 0;
3556 1637131 : for (auto snode : m_sg.m_nodes)
3557 1605898 : if (snode->get_function () == fn)
3558 183944 : ++num_supernodes;
3559 10411 : stats *new_stats = new stats (num_supernodes);
3560 10411 : m_per_function_stats.put (fn, new_stats);
3561 10411 : return new_stats;
3562 : }
3563 : }
3564 :
3565 : /* Print bar charts to PP showing:
3566 : - the number of enodes per function, and
3567 : - for each function:
3568 : - the number of enodes per supernode/BB
3569 : - the number of excess enodes per supernode/BB beyond the
3570 : per-program-point limit, if there were any. */
3571 :
3572 : void
3573 5 : exploded_graph::print_bar_charts (pretty_printer *pp) const
3574 : {
3575 5 : cgraph_node *cgnode;
3576 :
3577 5 : pp_string (pp, "enodes per function:");
3578 5 : pp_newline (pp);
3579 5 : bar_chart enodes_per_function;
3580 11 : FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (cgnode)
3581 : {
3582 6 : function *fn = cgnode->get_fun ();
3583 6 : const stats * const *s_ptr
3584 6 : = const_cast <function_stat_map_t &> (m_per_function_stats).get (fn);
3585 12 : enodes_per_function.add_item (function_name (fn),
3586 6 : s_ptr ? (*s_ptr)->get_total_enodes () : 0);
3587 : }
3588 5 : enodes_per_function.print (pp);
3589 :
3590 : /* Accumulate number of enodes per supernode. */
3591 10 : auto_vec<unsigned> enodes_per_supernode (m_sg.m_nodes.length ());
3592 170 : for (size_t i = 0; i < m_sg.m_nodes.length (); i++)
3593 80 : enodes_per_supernode.quick_push (0);
3594 : int i;
3595 : exploded_node *enode;
3596 207 : FOR_EACH_VEC_ELT (m_nodes, i, enode)
3597 : {
3598 202 : const supernode *iter_snode = enode->get_supernode ();
3599 202 : if (!iter_snode)
3600 5 : continue;
3601 197 : enodes_per_supernode[iter_snode->m_id]++;
3602 : }
3603 :
3604 : /* Accumulate excess enodes per supernode. */
3605 10 : auto_vec<unsigned> excess_enodes_per_supernode (m_sg.m_nodes.length ());
3606 85 : for (size_t i = 0; i < m_sg.m_nodes.length (); i++)
3607 80 : excess_enodes_per_supernode.quick_push (0);
3608 83 : for (point_map_t::iterator iter = m_per_point_data.begin ();
3609 161 : iter != m_per_point_data.end (); ++iter)
3610 : {
3611 78 : const program_point *point = (*iter).first;
3612 78 : const supernode *iter_snode = point->get_supernode ();
3613 78 : if (!iter_snode)
3614 5 : continue;
3615 73 : const per_program_point_data *point_data = (*iter).second;
3616 73 : excess_enodes_per_supernode[iter_snode->m_id]
3617 73 : += point_data->m_excess_enodes;
3618 : }
3619 :
3620 : /* Show per-function bar_charts of enodes per supernode/BB. */
3621 5 : pp_string (pp, "per-function enodes per supernode/BB:");
3622 5 : pp_newline (pp);
3623 11 : FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (cgnode)
3624 : {
3625 6 : function *fn = cgnode->get_fun ();
3626 6 : pp_printf (pp, "function: %qs", function_name (fn));
3627 6 : pp_newline (pp);
3628 :
3629 6 : bar_chart enodes_per_snode;
3630 6 : bar_chart excess_enodes_per_snode;
3631 6 : bool have_excess_enodes = false;
3632 112 : for (size_t i = 0; i < m_sg.m_nodes.length (); i++)
3633 : {
3634 106 : const supernode *iter_snode = m_sg.m_nodes[i];
3635 106 : if (iter_snode->get_function () != fn)
3636 26 : continue;
3637 80 : pretty_printer tmp_pp;
3638 80 : pp_printf (&tmp_pp, "sn %i (bb %i)",
3639 80 : iter_snode->m_id, iter_snode->m_bb->index);
3640 80 : enodes_per_snode.add_item (pp_formatted_text (&tmp_pp),
3641 80 : enodes_per_supernode[iter_snode->m_id]);
3642 80 : const int num_excess
3643 80 : = excess_enodes_per_supernode[iter_snode->m_id];
3644 80 : excess_enodes_per_snode.add_item (pp_formatted_text (&tmp_pp),
3645 : num_excess);
3646 80 : if (num_excess)
3647 0 : have_excess_enodes = true;
3648 80 : }
3649 6 : enodes_per_snode.print (pp);
3650 6 : if (have_excess_enodes)
3651 : {
3652 0 : pp_printf (pp, "EXCESS ENODES:");
3653 0 : pp_newline (pp);
3654 0 : excess_enodes_per_snode.print (pp);
3655 : }
3656 6 : }
3657 5 : }
3658 :
3659 : /* Write all stats information to this graph's logger, if any. */
3660 :
3661 : void
3662 3423 : exploded_graph::log_stats () const
3663 : {
3664 3423 : logger * const logger = get_logger ();
3665 3423 : if (!logger)
3666 3418 : return;
3667 :
3668 5 : LOG_SCOPE (logger);
3669 :
3670 5 : m_ext_state.get_engine ()->log_stats (logger);
3671 :
3672 10 : logger->log ("m_sg.num_nodes (): %i", m_sg.num_nodes ());
3673 10 : logger->log ("m_nodes.length (): %i", m_nodes.length ());
3674 10 : logger->log ("m_edges.length (): %i", m_edges.length ());
3675 5 : logger->log ("remaining enodes in worklist: %i", m_worklist.length ());
3676 :
3677 5 : logger->log ("global stats:");
3678 5 : m_global_stats.log (logger);
3679 :
3680 5 : for (function_stat_map_t::iterator iter = m_per_function_stats.begin ();
3681 22 : iter != m_per_function_stats.end ();
3682 6 : ++iter)
3683 : {
3684 6 : function *fn = (*iter).first;
3685 6 : log_scope s (logger, function_name (fn));
3686 6 : (*iter).second->log (logger);
3687 6 : }
3688 :
3689 5 : print_bar_charts (logger->get_printer ());
3690 5 : }
3691 :
3692 : /* Dump all stats information to OUT. */
3693 :
3694 : void
3695 0 : exploded_graph::dump_stats (FILE *out) const
3696 : {
3697 0 : fprintf (out, "m_sg.num_nodes (): %i\n", m_sg.num_nodes ());
3698 0 : fprintf (out, "m_nodes.length (): %i\n", m_nodes.length ());
3699 0 : fprintf (out, "m_edges.length (): %i\n", m_edges.length ());
3700 0 : fprintf (out, "remaining enodes in worklist: %i", m_worklist.length ());
3701 :
3702 0 : fprintf (out, "global stats:\n");
3703 0 : m_global_stats.dump (out);
3704 :
3705 0 : for (function_stat_map_t::iterator iter = m_per_function_stats.begin ();
3706 0 : iter != m_per_function_stats.end ();
3707 0 : ++iter)
3708 : {
3709 0 : function *fn = (*iter).first;
3710 0 : fprintf (out, "function: %s\n", function_name (fn));
3711 0 : (*iter).second->dump (out);
3712 : }
3713 0 : }
3714 :
3715 : /* Return a new json::object of the form
3716 : {"nodes" : [objs for enodes],
3717 : "edges" : [objs for eedges],
3718 : "ext_state": object for extrinsic_state,
3719 : "diagnostic_manager": object for diagnostic_manager}. */
3720 :
3721 : std::unique_ptr<json::object>
3722 0 : exploded_graph::to_json () const
3723 : {
3724 0 : auto egraph_obj = std::make_unique<json::object> ();
3725 :
3726 : /* Nodes. */
3727 0 : {
3728 0 : auto nodes_arr = std::make_unique<json::array> ();
3729 0 : unsigned i;
3730 0 : exploded_node *n;
3731 0 : FOR_EACH_VEC_ELT (m_nodes, i, n)
3732 0 : nodes_arr->append (n->to_json (m_ext_state));
3733 0 : egraph_obj->set ("nodes", std::move (nodes_arr));
3734 0 : }
3735 :
3736 : /* Edges. */
3737 0 : {
3738 0 : auto edges_arr = std::make_unique<json::array> ();
3739 0 : unsigned i;
3740 0 : exploded_edge *n;
3741 0 : FOR_EACH_VEC_ELT (m_edges, i, n)
3742 0 : edges_arr->append (n->to_json ());
3743 0 : egraph_obj->set ("edges", std::move (edges_arr));
3744 0 : }
3745 :
3746 : /* m_sg is JSONified at the top-level. */
3747 :
3748 0 : egraph_obj->set ("ext_state", m_ext_state.to_json ());
3749 0 : egraph_obj->set ("worklist", m_worklist.to_json ());
3750 0 : egraph_obj->set ("diagnostic_manager", m_diagnostic_manager.to_json ());
3751 :
3752 : /* The following fields aren't yet being JSONified:
3753 : const state_purge_map *const m_purge_map;
3754 : const analysis_plan &m_plan;
3755 : stats m_global_stats;
3756 : function_stat_map_t m_per_function_stats;
3757 : stats m_functionless_stats;
3758 : call_string_data_map_t m_per_call_string_data; */
3759 :
3760 0 : return egraph_obj;
3761 : }
3762 :
3763 : /* class feasibility_problem. */
3764 :
3765 : void
3766 4 : feasibility_problem::dump_to_pp (pretty_printer *pp) const
3767 : {
3768 4 : pp_printf (pp, "edge from EN: %i to EN: %i",
3769 4 : m_eedge.m_src->m_index, m_eedge.m_dest->m_index);
3770 4 : if (m_rc)
3771 : {
3772 4 : pp_string (pp, "; rejected constraint: ");
3773 4 : m_rc->dump_to_pp (pp);
3774 4 : pp_string (pp, "; rmodel: ");
3775 4 : m_rc->get_model ().dump_to_pp (pp, true, false);
3776 : }
3777 4 : }
3778 :
3779 : /* class feasibility_state. */
3780 :
3781 : /* Ctor for feasibility_state, at the beginning of a path. */
3782 :
3783 6615 : feasibility_state::feasibility_state (region_model_manager *manager,
3784 6615 : const supergraph &sg)
3785 6615 : : m_model (manager),
3786 13230 : m_snodes_visited (sg.m_nodes.length ())
3787 : {
3788 6615 : bitmap_clear (m_snodes_visited);
3789 6615 : }
3790 :
3791 : /* Copy ctor for feasibility_state, for extending a path. */
3792 :
3793 473119 : feasibility_state::feasibility_state (const feasibility_state &other)
3794 473119 : : m_model (other.m_model),
3795 473119 : m_snodes_visited (const_sbitmap (other.m_snodes_visited)->n_bits)
3796 : {
3797 473119 : bitmap_copy (m_snodes_visited, other.m_snodes_visited);
3798 473119 : }
3799 :
3800 32277 : feasibility_state::feasibility_state (const region_model &model,
3801 32277 : const supergraph &sg)
3802 32277 : : m_model (model),
3803 64554 : m_snodes_visited (sg.m_nodes.length ())
3804 : {
3805 32277 : bitmap_clear (m_snodes_visited);
3806 32277 : }
3807 :
3808 : feasibility_state &
3809 58665 : feasibility_state::operator= (const feasibility_state &other)
3810 : {
3811 58665 : m_model = other.m_model;
3812 58665 : bitmap_copy (m_snodes_visited, other.m_snodes_visited);
3813 58665 : return *this;
3814 : }
3815 :
3816 : /* The heart of feasibility-checking.
3817 :
3818 : Attempt to update this state in-place based on traversing EEDGE
3819 : in a path.
3820 : Update the model for the stmts in the src enode.
3821 : Attempt to add constraints for EEDGE.
3822 :
3823 : If feasible, return true.
3824 : Otherwise, return false and write to *OUT_RC. */
3825 :
3826 : bool
3827 206673 : feasibility_state::
3828 : maybe_update_for_edge (logger *logger,
3829 : const exploded_edge *eedge,
3830 : region_model_context *ctxt,
3831 : std::unique_ptr<rejected_constraint> *out_rc)
3832 : {
3833 206673 : const exploded_node &src_enode = *eedge->m_src;
3834 206673 : const program_point &src_point = src_enode.get_point ();
3835 206673 : if (logger)
3836 : {
3837 82 : logger->start_log_line ();
3838 82 : src_point.print (logger->get_printer (), format (false));
3839 82 : logger->end_log_line ();
3840 : }
3841 :
3842 206673 : if (eedge->m_custom_info)
3843 9405 : eedge->m_custom_info->update_model (&m_model, eedge, ctxt);
3844 : else
3845 : {
3846 197268 : const superedge *sedge = eedge->m_sedge;
3847 197268 : if (sedge)
3848 : {
3849 189127 : if (logger)
3850 : {
3851 80 : label_text desc (sedge->get_description (false));
3852 80 : logger->log (" sedge: SN:%i -> SN:%i %s",
3853 80 : sedge->m_src->m_id,
3854 80 : sedge->m_dest->m_id,
3855 : desc.get ());
3856 80 : }
3857 :
3858 189127 : if (sedge->get_op ())
3859 150846 : if (!sedge->get_op ()->execute_for_feasibility (*eedge,
3860 : *this,
3861 : ctxt,
3862 : out_rc))
3863 : {
3864 6777 : if (logger)
3865 : {
3866 8 : logger->start_log_line ();
3867 8 : logger->log_partial ("rejecting due to region model: ");
3868 8 : m_model.dump_to_pp (logger->get_printer (), true, false);
3869 8 : logger->end_log_line ();
3870 : }
3871 6777 : return false;
3872 : }
3873 : }
3874 : else
3875 : {
3876 : /* Special-case the initial eedge from the origin node to the
3877 : initial function by pushing a frame for it. */
3878 8141 : if (eedge->m_src->m_index == 0)
3879 : {
3880 6442 : function *fun = eedge->m_dest->get_function ();
3881 6442 : gcc_assert (fun);
3882 6442 : m_model.push_frame (*fun, nullptr, nullptr, ctxt);
3883 6442 : if (logger)
3884 2 : logger->log (" pushing frame for %qD", fun->decl);
3885 : }
3886 : }
3887 : }
3888 :
3889 :
3890 199896 : {
3891 199896 : const exploded_node &dst_enode = *eedge->m_dest;
3892 199896 : const unsigned dst_snode_idx = dst_enode.get_supernode ()->m_id;
3893 199896 : bitmap_set_bit (m_snodes_visited, dst_snode_idx);
3894 : }
3895 :
3896 199896 : return true;
3897 : }
3898 :
3899 : /* Dump this object to PP. */
3900 :
3901 : void
3902 70 : feasibility_state::dump_to_pp (pretty_printer *pp,
3903 : bool simple, bool multiline) const
3904 : {
3905 70 : m_model.dump_to_pp (pp, simple, multiline);
3906 70 : }
3907 :
3908 : /* A family of cluster subclasses for use when generating .dot output for
3909 : exploded graphs (-fdump-analyzer-exploded-graph), for grouping the
3910 : enodes into hierarchical boxes.
3911 :
3912 : All functionless enodes appear in the top-level graph.
3913 : Every (function, call_string) pair gets its own cluster. Within that
3914 : cluster, each supernode gets its own cluster.
3915 :
3916 : Hence all enodes relating to a particular function with a particular
3917 : callstring will be in a cluster together; all enodes for the same
3918 : function but with a different callstring will be in a different
3919 : cluster. */
3920 :
3921 : /* Base class of cluster for clustering exploded_node instances in .dot
3922 : output, based on various subclass-specific criteria. */
3923 :
3924 1291 : class exploded_cluster : public cluster<eg_traits>
3925 : {
3926 : };
3927 :
3928 : /* Cluster containing all exploded_node instances for one supernode. */
3929 :
3930 : class supernode_cluster : public exploded_cluster
3931 : {
3932 : public:
3933 429 : supernode_cluster (const supernode *supernode) : m_supernode (supernode) {}
3934 :
3935 : // TODO: dtor?
3936 :
3937 429 : void dump_dot (graphviz_out *gv, const dump_args_t &args) const final override
3938 : {
3939 429 : gv->println ("subgraph \"cluster_supernode_%i\" {", m_supernode->m_id);
3940 429 : gv->indent ();
3941 429 : gv->println ("style=\"dashed\";");
3942 858 : gv->println ("label=\"SN: %i (bb: %i; scc: %i)\";",
3943 429 : m_supernode->m_id, m_supernode->m_bb->index,
3944 429 : args.m_eg.get_scc_id (*m_supernode));
3945 :
3946 429 : int i;
3947 429 : exploded_node *enode;
3948 1287 : FOR_EACH_VEC_ELT (m_enodes, i, enode)
3949 429 : enode->dump_dot (gv, args);
3950 :
3951 : /* Terminate subgraph. */
3952 429 : gv->outdent ();
3953 429 : gv->println ("}");
3954 429 : }
3955 :
3956 429 : void add_node (exploded_node *en) final override
3957 : {
3958 0 : m_enodes.safe_push (en);
3959 0 : }
3960 :
3961 : /* Comparator for use by auto_vec<supernode_cluster *>::qsort. */
3962 :
3963 0 : static int cmp_ptr_ptr (const void *p1, const void *p2)
3964 : {
3965 0 : const supernode_cluster *c1
3966 : = *(const supernode_cluster * const *)p1;
3967 0 : const supernode_cluster *c2
3968 : = *(const supernode_cluster * const *)p2;
3969 0 : return c1->m_supernode->m_id - c2->m_supernode->m_id;
3970 : }
3971 :
3972 : private:
3973 : const supernode *m_supernode;
3974 : auto_vec <exploded_node *> m_enodes;
3975 : };
3976 :
3977 : /* Cluster containing all supernode_cluster instances for one
3978 : (function, call_string) pair. */
3979 :
3980 : class function_call_string_cluster : public exploded_cluster
3981 : {
3982 : public:
3983 429 : function_call_string_cluster (function *fun, const call_string &cs)
3984 429 : : m_fun (fun), m_cs (cs) {}
3985 :
3986 858 : ~function_call_string_cluster ()
3987 429 : {
3988 429 : for (map_t::iterator iter = m_map.begin ();
3989 1716 : iter != m_map.end ();
3990 429 : ++iter)
3991 429 : delete (*iter).second;
3992 858 : }
3993 :
3994 429 : void dump_dot (graphviz_out *gv, const dump_args_t &args) const final override
3995 : {
3996 429 : const char *funcname = function_name (m_fun);
3997 :
3998 858 : gv->println ("subgraph \"cluster_function_%s\" {",
3999 429 : IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (m_fun->decl)));
4000 429 : gv->indent ();
4001 429 : gv->write_indent ();
4002 429 : gv->print ("label=\"call string: ");
4003 429 : m_cs.print (gv->get_pp ());
4004 429 : gv->print (" function: %s \";", funcname);
4005 429 : gv->print ("\n");
4006 :
4007 : /* Dump m_map, sorting it to avoid churn when comparing dumps. */
4008 429 : auto_vec<supernode_cluster *> child_clusters (m_map.elements ());
4009 429 : for (map_t::iterator iter = m_map.begin ();
4010 1716 : iter != m_map.end ();
4011 429 : ++iter)
4012 429 : child_clusters.quick_push ((*iter).second);
4013 :
4014 429 : child_clusters.qsort (supernode_cluster::cmp_ptr_ptr);
4015 :
4016 : unsigned i;
4017 : supernode_cluster *child_cluster;
4018 858 : FOR_EACH_VEC_ELT (child_clusters, i, child_cluster)
4019 429 : child_cluster->dump_dot (gv, args);
4020 :
4021 : /* Terminate subgraph. */
4022 429 : gv->outdent ();
4023 429 : gv->println ("}");
4024 429 : }
4025 :
4026 429 : void add_node (exploded_node *en) final override
4027 : {
4028 429 : const supernode *supernode = en->get_supernode ();
4029 429 : gcc_assert (supernode);
4030 429 : supernode_cluster **slot = m_map.get (supernode);
4031 429 : if (slot)
4032 0 : (*slot)->add_node (en);
4033 : else
4034 : {
4035 429 : supernode_cluster *child = new supernode_cluster (supernode);
4036 429 : m_map.put (supernode, child);
4037 429 : child->add_node (en);
4038 : }
4039 429 : }
4040 :
4041 : /* Comparator for use by auto_vec<function_call_string_cluster *>. */
4042 :
4043 17679 : static int cmp_ptr_ptr (const void *p1, const void *p2)
4044 : {
4045 17679 : const function_call_string_cluster *c1
4046 : = *(const function_call_string_cluster * const *)p1;
4047 17679 : const function_call_string_cluster *c2
4048 : = *(const function_call_string_cluster * const *)p2;
4049 35358 : if (int cmp_names
4050 17679 : = strcmp (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (c1->m_fun->decl)),
4051 17679 : IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (c2->m_fun->decl))))
4052 : return cmp_names;
4053 5002 : return call_string::cmp (c1->m_cs, c2->m_cs);
4054 : }
4055 :
4056 : private:
4057 : function *m_fun;
4058 : const call_string &m_cs;
4059 : typedef ordered_hash_map<const supernode *, supernode_cluster *> map_t;
4060 : map_t m_map;
4061 : };
4062 :
4063 : /* Keys for root_cluster. */
4064 :
4065 : struct function_call_string
4066 : {
4067 429 : function_call_string (function *fun, const call_string *cs)
4068 429 : : m_fun (fun), m_cs (cs)
4069 : {
4070 429 : gcc_assert (fun);
4071 429 : gcc_assert (cs);
4072 429 : }
4073 :
4074 : function *m_fun;
4075 : const call_string *m_cs;
4076 : };
4077 :
4078 : } // namespace ana
4079 :
4080 : template <> struct default_hash_traits<function_call_string>
4081 : : public pod_hash_traits<function_call_string>
4082 : {
4083 : static const bool empty_zero_p = false;
4084 : };
4085 :
4086 : template <>
4087 : inline hashval_t
4088 5941 : pod_hash_traits<function_call_string>::hash (value_type v)
4089 : {
4090 5941 : return (pointer_hash <function>::hash (v.m_fun)
4091 5941 : ^ pointer_hash <const call_string>::hash (v.m_cs));
4092 : }
4093 :
4094 : template <>
4095 : inline bool
4096 30353 : pod_hash_traits<function_call_string>::equal (const value_type &existing,
4097 : const value_type &candidate)
4098 : {
4099 30353 : return existing.m_fun == candidate.m_fun && &existing.m_cs == &candidate.m_cs;
4100 : }
4101 : template <>
4102 : inline void
4103 : pod_hash_traits<function_call_string>::mark_deleted (value_type &v)
4104 : {
4105 : v.m_fun = reinterpret_cast<function *> (1);
4106 : }
4107 : template <>
4108 : inline void
4109 1932 : pod_hash_traits<function_call_string>::mark_empty (value_type &v)
4110 : {
4111 1932 : v.m_fun = nullptr;
4112 : }
4113 : template <>
4114 : inline bool
4115 48035 : pod_hash_traits<function_call_string>::is_deleted (value_type v)
4116 : {
4117 48035 : return v.m_fun == reinterpret_cast<function *> (1);
4118 : }
4119 : template <>
4120 : inline bool
4121 105927 : pod_hash_traits<function_call_string>::is_empty (value_type v)
4122 : {
4123 105498 : return v.m_fun == nullptr;
4124 : }
4125 :
4126 : namespace ana {
4127 :
4128 : /* Top-level cluster for generating .dot output for exploded graphs,
4129 : handling the functionless nodes, and grouping the remaining nodes by
4130 : callstring. */
4131 :
4132 : class root_cluster : public exploded_cluster
4133 : {
4134 : public:
4135 4 : ~root_cluster ()
4136 4 : {
4137 433 : for (map_t::iterator iter = m_map.begin ();
4138 433 : iter != m_map.end ();
4139 429 : ++iter)
4140 429 : delete (*iter).second;
4141 4 : }
4142 :
4143 4 : void dump_dot (graphviz_out *gv, const dump_args_t &args) const final override
4144 : {
4145 4 : int i;
4146 4 : exploded_node *enode;
4147 8 : FOR_EACH_VEC_ELT (m_functionless_enodes, i, enode)
4148 4 : enode->dump_dot (gv, args);
4149 :
4150 : /* Dump m_map, sorting it to avoid churn when comparing dumps. */
4151 4 : auto_vec<function_call_string_cluster *> child_clusters (m_map.elements ());
4152 4 : for (map_t::iterator iter = m_map.begin ();
4153 433 : iter != m_map.end ();
4154 429 : ++iter)
4155 429 : child_clusters.quick_push ((*iter).second);
4156 :
4157 4 : child_clusters.qsort (function_call_string_cluster::cmp_ptr_ptr);
4158 :
4159 : function_call_string_cluster *child_cluster;
4160 437 : FOR_EACH_VEC_ELT (child_clusters, i, child_cluster)
4161 429 : child_cluster->dump_dot (gv, args);
4162 4 : }
4163 :
4164 433 : void add_node (exploded_node *en) final override
4165 : {
4166 433 : function *fun = en->get_function ();
4167 429 : if (!fun)
4168 : {
4169 4 : m_functionless_enodes.safe_push (en);
4170 4 : return;
4171 : }
4172 :
4173 429 : const call_string &cs = en->get_point ().get_call_string ();
4174 429 : function_call_string key (fun, &cs);
4175 429 : function_call_string_cluster **slot = m_map.get (key);
4176 429 : if (slot)
4177 0 : (*slot)->add_node (en);
4178 : else
4179 : {
4180 429 : function_call_string_cluster *child
4181 429 : = new function_call_string_cluster (fun, cs);
4182 429 : m_map.put (key, child);
4183 429 : child->add_node (en);
4184 : }
4185 : }
4186 :
4187 : private:
4188 : typedef hash_map<function_call_string, function_call_string_cluster *> map_t;
4189 : map_t m_map;
4190 :
4191 : /* This should just be the origin exploded_node. */
4192 : auto_vec <exploded_node *> m_functionless_enodes;
4193 : };
4194 :
4195 : /* Subclass of range_label for use within
4196 : exploded_graph::dump_exploded_nodes for implementing
4197 : -fdump-analyzer-exploded-nodes: a label for a specific
4198 : exploded_node. */
4199 :
4200 : class enode_label : public range_label
4201 : {
4202 : public:
4203 0 : enode_label (const extrinsic_state &ext_state,
4204 : exploded_node *enode)
4205 0 : : m_ext_state (ext_state), m_enode (enode) {}
4206 :
4207 0 : label_text get_text (unsigned) const final override
4208 : {
4209 0 : pretty_printer pp;
4210 0 : pp_format_decoder (&pp) = default_tree_printer;
4211 0 : m_enode->get_state ().dump_to_pp (m_ext_state, true, false, &pp);
4212 0 : return make_label_text (false, "EN: %i: %s",
4213 0 : m_enode->m_index, pp_formatted_text (&pp));
4214 0 : }
4215 :
4216 : private:
4217 : const extrinsic_state &m_ext_state;
4218 : exploded_node *m_enode;
4219 : };
4220 :
4221 : /* Postprocessing support for dumping the exploded nodes.
4222 : Handle -fdump-analyzer-exploded-nodes,
4223 : -fdump-analyzer-exploded-nodes-2, and the
4224 : "__analyzer_dump_exploded_nodes" builtin. */
4225 :
4226 : void
4227 3423 : exploded_graph::dump_exploded_nodes () const
4228 : {
4229 : // TODO
4230 : /* Locate calls to __analyzer_dump_exploded_nodes. */
4231 : // Print how many egs there are for them?
4232 : /* Better: log them as we go, and record the exploded nodes
4233 : in question. */
4234 :
4235 : /* Show every enode. */
4236 :
4237 : /* Gather them by stmt, so that we can more clearly see the
4238 : "hotspots" requiring numerous exploded nodes. */
4239 :
4240 : /* Alternatively, simply throw them all into one big rich_location
4241 : and see if the label-printing will sort it out...
4242 : This requires them all to be in the same source file. */
4243 :
4244 3423 : if (flag_dump_analyzer_exploded_nodes)
4245 : {
4246 0 : auto_timevar tv (TV_ANALYZER_DUMP);
4247 0 : gcc_rich_location richloc (UNKNOWN_LOCATION);
4248 0 : unsigned i;
4249 0 : exploded_node *enode;
4250 0 : FOR_EACH_VEC_ELT (m_nodes, i, enode)
4251 : {
4252 0 : location_t loc = enode->get_location ();
4253 0 : if (get_pure_location (richloc.get_loc ()) == UNKNOWN_LOCATION)
4254 0 : richloc.set_range (0, loc, SHOW_RANGE_WITH_CARET);
4255 : else
4256 0 : richloc.add_range (loc,
4257 : SHOW_RANGE_WITHOUT_CARET,
4258 0 : new enode_label (m_ext_state, enode));
4259 : }
4260 0 : warning_at (&richloc, 0, "%i exploded nodes", m_nodes.length ());
4261 :
4262 : /* Repeat the warning without all the labels, so that message is visible
4263 : (the other one may well have scrolled past the terminal limit). */
4264 0 : warning_at (richloc.get_loc (), 0,
4265 : "%i exploded nodes", m_nodes.length ());
4266 :
4267 0 : if (m_worklist.length () > 0)
4268 0 : warning_at (richloc.get_loc (), 0,
4269 : "worklist still contains %i nodes", m_worklist.length ());
4270 0 : }
4271 :
4272 : /* Dump the egraph in textual form to a dump file. */
4273 3423 : if (flag_dump_analyzer_exploded_nodes_2)
4274 : {
4275 0 : auto_timevar tv (TV_ANALYZER_DUMP);
4276 0 : char *filename
4277 0 : = concat (dump_base_name, ".eg.txt", nullptr);
4278 0 : FILE *outf = fopen (filename, "w");
4279 0 : if (!outf)
4280 0 : error_at (UNKNOWN_LOCATION, "unable to open %qs for writing", filename);
4281 0 : free (filename);
4282 :
4283 0 : fprintf (outf, "exploded graph for %s\n", dump_base_name);
4284 0 : fprintf (outf, " nodes: %i\n", m_nodes.length ());
4285 0 : fprintf (outf, " edges: %i\n", m_edges.length ());
4286 :
4287 0 : unsigned i;
4288 0 : exploded_node *enode;
4289 0 : FOR_EACH_VEC_ELT (m_nodes, i, enode)
4290 : {
4291 0 : fprintf (outf, "\nEN %i:\n", enode->m_index);
4292 0 : enode->dump_succs_and_preds (outf);
4293 0 : pretty_printer pp;
4294 0 : enode->get_point ().print (&pp, format (true));
4295 0 : fprintf (outf, "%s\n", pp_formatted_text (&pp));
4296 0 : text_art::dump_to_file (enode->get_state (), outf);
4297 0 : }
4298 :
4299 0 : fclose (outf);
4300 0 : }
4301 :
4302 : /* Dump the egraph in textual form to multiple dump files, one per enode. */
4303 3423 : if (flag_dump_analyzer_exploded_nodes_3)
4304 : {
4305 0 : auto_timevar tv (TV_ANALYZER_DUMP);
4306 :
4307 0 : unsigned i;
4308 0 : exploded_node *enode;
4309 0 : FOR_EACH_VEC_ELT (m_nodes, i, enode)
4310 : {
4311 0 : char *filename
4312 0 : = xasprintf ("%s.en-%i.txt", dump_base_name, i);
4313 0 : FILE *outf = fopen (filename, "w");
4314 0 : if (!outf)
4315 0 : error_at (UNKNOWN_LOCATION, "unable to open %qs for writing",
4316 : filename);
4317 0 : free (filename);
4318 :
4319 0 : fprintf (outf, "EN %i:\n", enode->m_index);
4320 0 : enode->dump_succs_and_preds (outf);
4321 0 : pretty_printer pp;
4322 0 : enode->get_point ().print (&pp, format (true));
4323 0 : fprintf (outf, "%s\n", pp_formatted_text (&pp));
4324 0 : text_art::dump_to_file (enode->get_state (), outf);
4325 :
4326 0 : fclose (outf);
4327 0 : }
4328 0 : }
4329 :
4330 : /* Emit a warning at any call to "__analyzer_dump_exploded_nodes",
4331 : giving the number of processed exploded nodes at the snode before
4332 : the call, and the IDs of processed, merger, and worklist enodes.
4333 :
4334 : We highlight the count of *processed* enodes since this is of most
4335 : interest in DejaGnu tests for ensuring that state merger has
4336 : happened.
4337 :
4338 : We don't show the count of merger and worklist enodes, as this is
4339 : more of an implementation detail of the merging/worklist that we
4340 : don't want to bake into our expected DejaGnu messages. */
4341 :
4342 3423 : unsigned i;
4343 3423 : exploded_node *enode;
4344 3423 : hash_set<const gimple *> seen;
4345 399256 : FOR_EACH_VEC_ELT (m_nodes, i, enode)
4346 : {
4347 392414 : const supernode *snode = enode->get_supernode ();
4348 392414 : if (!snode)
4349 3419 : continue;
4350 388995 : if (snode->m_succs.length () != 1)
4351 43872 : continue;
4352 345123 : const superedge *sedge = snode->m_succs[0];
4353 345123 : if (!sedge->get_op ())
4354 81368 : continue;
4355 263755 : const call_and_return_op *op
4356 263755 : = sedge->get_op ()->dyn_cast_call_and_return_op ();
4357 263755 : if (!op)
4358 201922 : continue;
4359 61833 : const gcall &call = op->get_gcall ();
4360 61833 : if (is_special_named_call_p (call, "__analyzer_dump_exploded_nodes", 1))
4361 : {
4362 1129 : if (seen.contains (&call))
4363 444 : continue;
4364 :
4365 685 : auto_vec<exploded_node *> processed_enodes;
4366 685 : auto_vec<exploded_node *> merger_enodes;
4367 685 : auto_vec<exploded_node *> worklist_enodes;
4368 : /* This is O(N^2). */
4369 685 : unsigned j;
4370 685 : exploded_node *other_enode;
4371 121428 : FOR_EACH_VEC_ELT (m_nodes, j, other_enode)
4372 : {
4373 120743 : if (other_enode->get_supernode () == snode)
4374 1129 : switch (other_enode->get_status ())
4375 : {
4376 0 : default:
4377 0 : gcc_unreachable ();
4378 0 : case exploded_node::status::worklist:
4379 0 : worklist_enodes.safe_push (other_enode);
4380 0 : break;
4381 1060 : case exploded_node::status::processed:
4382 1060 : processed_enodes.safe_push (other_enode);
4383 1060 : break;
4384 69 : case exploded_node::status::merger:
4385 69 : merger_enodes.safe_push (other_enode);
4386 69 : break;
4387 : }
4388 : }
4389 :
4390 1370 : pretty_printer pp;
4391 685 : pp_character (&pp, '[');
4392 685 : print_enode_indices (&pp, processed_enodes);
4393 685 : if (merger_enodes.length () > 0)
4394 : {
4395 44 : pp_string (&pp, "] merger(s): [");
4396 44 : print_enode_indices (&pp, merger_enodes);
4397 : }
4398 685 : if (worklist_enodes.length () > 0)
4399 : {
4400 0 : pp_string (&pp, "] worklist: [");
4401 0 : print_enode_indices (&pp, worklist_enodes);
4402 : }
4403 685 : pp_character (&pp, ']');
4404 :
4405 1370 : warning_n (call.location, 0, processed_enodes.length (),
4406 : "%i processed enode: %s",
4407 : "%i processed enodes: %s",
4408 : processed_enodes.length (), pp_formatted_text (&pp));
4409 685 : seen.add (&call);
4410 :
4411 : /* If the argument is non-zero, then print all of the states
4412 : of the various enodes. */
4413 685 : tree t_arg = fold (gimple_call_arg (&call, 0));
4414 685 : if (TREE_CODE (t_arg) != INTEGER_CST)
4415 : {
4416 0 : error_at (snode->m_loc,
4417 : "integer constant required for arg 1");
4418 0 : return;
4419 : }
4420 685 : int i_arg = TREE_INT_CST_LOW (t_arg);
4421 685 : if (i_arg)
4422 : {
4423 : exploded_node *other_enode;
4424 685 : FOR_EACH_VEC_ELT (processed_enodes, j, other_enode)
4425 : {
4426 0 : fprintf (stderr, "%i of %i: EN %i:\n",
4427 : j + 1, processed_enodes.length (),
4428 0 : other_enode->m_index);
4429 0 : other_enode->dump_succs_and_preds (stderr);
4430 : /* Dump state. */
4431 0 : other_enode->get_state ().dump (m_ext_state, false);
4432 : }
4433 : }
4434 685 : }
4435 : }
4436 3423 : }
4437 :
4438 : DEBUG_FUNCTION exploded_node *
4439 0 : exploded_graph::get_node_by_index (int idx) const
4440 : {
4441 0 : exploded_node *enode = m_nodes[idx];
4442 0 : gcc_assert (enode->m_index == idx);
4443 0 : return enode;
4444 : }
4445 :
4446 : /* Ensure that there is an exploded_node for a top-level call to FNDECL. */
4447 :
4448 : void
4449 298 : exploded_graph::on_escaped_function (tree fndecl)
4450 : {
4451 298 : logger * const logger = get_logger ();
4452 298 : LOG_FUNC_1 (logger, "%qE", fndecl);
4453 :
4454 298 : cgraph_node *cgnode = cgraph_node::get (fndecl);
4455 298 : if (!cgnode)
4456 : return;
4457 :
4458 298 : function *fun = cgnode->get_fun ();
4459 298 : if (!fun)
4460 : return;
4461 :
4462 294 : if (!gimple_has_body_p (fndecl))
4463 : return;
4464 :
4465 294 : exploded_node *enode = add_function_entry (*fun);
4466 294 : if (logger)
4467 : {
4468 0 : if (enode)
4469 0 : logger->log ("created EN %i for %qE entrypoint",
4470 0 : enode->m_index, fun->decl);
4471 : else
4472 0 : logger->log ("did not create enode for %qE entrypoint", fun->decl);
4473 : }
4474 298 : }
4475 :
4476 : /* Subclass of dot_annotator for implementing
4477 : DUMP_BASE_NAME.supergraph.N.eg.dot, a post-analysis dump of the supergraph.
4478 :
4479 : Annotate the supergraph nodes by printing the exploded nodes in concise
4480 : form within them, colorizing the exploded nodes based on sm-state.
4481 : Also show saved diagnostics within the exploded nodes, giving information
4482 : on whether they were feasible, and, if infeasible, where the problem
4483 : was. */
4484 :
4485 4 : class exploded_graph_annotator : public dot_annotator
4486 : {
4487 : public:
4488 4 : exploded_graph_annotator (const exploded_graph &eg)
4489 4 : : m_eg (eg)
4490 : {
4491 : /* Avoid O(N^2) by prepopulating m_enodes_per_snode_id. */
4492 390 : for (size_t i = 0; i < eg.get_supergraph ().m_nodes.length (); ++i)
4493 191 : m_enodes_per_snode_id.push_back (std::vector<exploded_node *> ());
4494 : exploded_node *enode;
4495 : unsigned i;
4496 437 : FOR_EACH_VEC_ELT (m_eg.m_nodes, i, enode)
4497 433 : if (enode->get_supernode ())
4498 429 : m_enodes_per_snode_id[enode->get_supernode ()->m_id].push_back (enode);
4499 4 : }
4500 :
4501 : /* Show exploded nodes for N. */
4502 191 : void add_node_annotations (graphviz_out *gv, const supernode &n)
4503 : const final override
4504 : {
4505 191 : gv->begin_tr ();
4506 191 : pretty_printer *pp = gv->get_pp ();
4507 :
4508 191 : if (m_enodes_per_snode_id[n.m_id].empty ())
4509 4 : pp_string (pp, "<TD BGCOLOR=\"red\">UNREACHED</TD>");
4510 : else
4511 : {
4512 : /* Adding an empty TD here makes the actual enodes
4513 : be right-aligned and tightly packed, greatly
4514 : improving the readability of the graph. */
4515 187 : pp_string (pp, "<TD></TD>");
4516 616 : for (auto enode : m_enodes_per_snode_id[n.m_id])
4517 : {
4518 429 : gcc_assert (enode->get_supernode () == &n);
4519 429 : print_enode (gv, enode);
4520 : }
4521 : }
4522 :
4523 191 : pp_flush (pp);
4524 191 : gv->end_tr ();
4525 191 : }
4526 :
4527 : void
4528 4 : add_extra_objects (graphviz_out *gv) const final override
4529 : {
4530 4 : pretty_printer *pp = gv->get_pp ();
4531 :
4532 4 : pp_string (pp, "en_0 [shape=none,margin=0,style=filled,label=<<TABLE><TR>");
4533 4 : print_enode (gv, m_eg.m_nodes[0]);
4534 4 : pp_string (pp, "</TR></TABLE>>];\n\n");
4535 4 : pp_flush (pp);
4536 :
4537 4 : unsigned i;
4538 4 : exploded_edge *eedge;
4539 449 : FOR_EACH_VEC_ELT (m_eg.m_edges, i, eedge)
4540 : {
4541 445 : print_enode_port (pp, *eedge->m_src, "s");
4542 445 : pp_string (pp, " -> ");
4543 445 : print_enode_port (pp, *eedge->m_dest, "n");
4544 445 : dot::attr_list attrs;
4545 445 : attrs.add (dot::id ("style"), dot::id ("dotted"));
4546 445 : if (eedge->m_custom_info)
4547 : {
4548 22 : pretty_printer info_pp;
4549 22 : pp_format_decoder (&info_pp) = default_tree_printer;
4550 22 : eedge->m_custom_info->print (&info_pp);
4551 44 : attrs.add (dot::id ("label"),
4552 44 : dot::id (pp_formatted_text (&info_pp)));
4553 22 : }
4554 445 : dot::writer w (*pp);
4555 445 : attrs.print (w);
4556 445 : pp_newline (pp);
4557 445 : }
4558 4 : }
4559 :
4560 : private:
4561 : void
4562 890 : print_enode_port (pretty_printer *pp,
4563 : const exploded_node &enode,
4564 : const char *compass_pt) const
4565 : {
4566 890 : if (const supernode *snode = enode.get_supernode ())
4567 878 : pp_printf (pp, "node_%i:en_%i:%s",
4568 878 : snode->m_id, enode.m_index, compass_pt);
4569 : else
4570 12 : pp_printf (pp, "en_%i:%s",
4571 12 : enode.m_index, compass_pt);
4572 890 : }
4573 :
4574 : /* Concisely print a TD element for ENODE, showing the index, status,
4575 : and any saved_diagnostics at the enode. Colorize it to show sm-state.
4576 :
4577 : Ideally we'd dump ENODE's state here, hidden behind some kind of
4578 : interactive disclosure method like a tooltip, so that the states
4579 : can be explored without overwhelming the graph.
4580 : However, I wasn't able to get graphviz/xdot to show tooltips on
4581 : individual elements within a HTML-like label. */
4582 433 : void print_enode (graphviz_out *gv, const exploded_node *enode) const
4583 : {
4584 433 : pretty_printer *pp = gv->get_pp ();
4585 433 : pp_printf (pp, "<TD BGCOLOR=\"%s\">",
4586 : enode->get_dot_fillcolor ());
4587 433 : pp_printf (pp, "<TABLE BORDER=\"0\" PORT=\"en_%i\">", enode->m_index);
4588 433 : gv->begin_trtd ();
4589 433 : pp_printf (pp, "EN: %i", enode->m_index);
4590 433 : switch (enode->get_status ())
4591 : {
4592 0 : default:
4593 0 : gcc_unreachable ();
4594 0 : case exploded_node::status::worklist:
4595 0 : pp_string (pp, "(W)");
4596 0 : break;
4597 : case exploded_node::status::processed:
4598 : break;
4599 6 : case exploded_node::status::special:
4600 6 : pp_string (pp, "(S)");
4601 6 : break;
4602 12 : case exploded_node::status::merger:
4603 12 : pp_string (pp, "(M)");
4604 12 : break;
4605 0 : case exploded_node::status::bulk_merged:
4606 0 : pp_string (pp, "(BM)");
4607 0 : break;
4608 : }
4609 433 : gv->end_tdtr ();
4610 :
4611 : /* Dump any saved_diagnostics at this enode. */
4612 437 : for (unsigned i = 0; i < enode->get_num_diagnostics (); i++)
4613 : {
4614 4 : const saved_diagnostic *sd = enode->get_saved_diagnostic (i);
4615 4 : print_saved_diagnostic (gv, sd);
4616 : }
4617 433 : pp_printf (pp, "</TABLE>");
4618 433 : pp_printf (pp, "</TD>");
4619 433 : }
4620 :
4621 : /* Print a TABLE element for SD, showing the kind, the length of the
4622 : exploded_path, whether the path was feasible, and if infeasible,
4623 : what the problem was. */
4624 4 : void print_saved_diagnostic (graphviz_out *gv,
4625 : const saved_diagnostic *sd) const
4626 : {
4627 4 : pretty_printer *pp = gv->get_pp ();
4628 4 : gv->begin_trtd ();
4629 4 : pp_printf (pp, "<TABLE BORDER=\"0\">");
4630 4 : gv->begin_tr ();
4631 4 : pp_string (pp, "<TD BGCOLOR=\"green\">");
4632 4 : pp_printf (pp, "DIAGNOSTIC: %s", sd->m_d->get_kind ());
4633 4 : gv->end_tdtr ();
4634 4 : gv->begin_trtd ();
4635 4 : if (sd->get_best_epath ())
4636 4 : pp_printf (pp, "epath length: %i", sd->get_epath_length ());
4637 : else
4638 0 : pp_printf (pp, "no best epath");
4639 4 : gv->end_tdtr ();
4640 4 : if (const feasibility_problem *p = sd->get_feasibility_problem ())
4641 : {
4642 0 : gv->begin_trtd ();
4643 0 : pp_printf (pp, "INFEASIBLE at eedge %i: EN:%i -> EN:%i",
4644 0 : p->m_eedge_idx,
4645 0 : p->m_eedge.m_src->m_index,
4646 0 : p->m_eedge.m_dest->m_index);
4647 0 : pp_write_text_as_html_like_dot_to_stream (pp);
4648 0 : gv->end_tdtr ();
4649 0 : gv->begin_trtd ();
4650 0 : p->m_eedge.m_sedge->dump (pp);
4651 0 : pp_write_text_as_html_like_dot_to_stream (pp);
4652 0 : gv->end_tdtr ();
4653 : /* Ideally we'd print p->m_model here; see the notes above about
4654 : tooltips. */
4655 : }
4656 4 : pp_printf (pp, "</TABLE>");
4657 4 : gv->end_tdtr ();
4658 4 : }
4659 :
4660 : const exploded_graph &m_eg;
4661 : std::vector<std::vector <exploded_node *> > m_enodes_per_snode_id;
4662 : };
4663 :
4664 : /* Implement -fdump-analyzer-json. */
4665 :
4666 : static void
4667 0 : dump_analyzer_json (const supergraph &sg,
4668 : const exploded_graph &eg)
4669 : {
4670 0 : auto_timevar tv (TV_ANALYZER_DUMP);
4671 0 : char *filename = concat (dump_base_name, ".analyzer.json.gz", nullptr);
4672 0 : gzFile output = gzopen (filename, "w");
4673 0 : if (!output)
4674 : {
4675 0 : error_at (UNKNOWN_LOCATION, "unable to open %qs for writing", filename);
4676 0 : free (filename);
4677 0 : return;
4678 : }
4679 :
4680 0 : auto toplev_obj = std::make_unique<json::object> ();
4681 0 : toplev_obj->set ("sgraph", sg.to_json ());
4682 0 : toplev_obj->set ("egraph", eg.to_json ());
4683 :
4684 0 : pretty_printer pp;
4685 0 : toplev_obj->print (&pp, flag_diagnostics_json_formatting);
4686 0 : pp_formatted_text (&pp);
4687 :
4688 0 : if (gzputs (output, pp_formatted_text (&pp)) == EOF
4689 0 : || gzclose (output))
4690 0 : error_at (UNKNOWN_LOCATION, "error writing %qs", filename);
4691 :
4692 0 : free (filename);
4693 0 : }
4694 :
4695 : /* Concrete subclass of on_ana_init, allowing plugins to register
4696 : new state machines. */
4697 :
4698 : class impl_on_ana_init : public gcc::topics::analyzer_events::on_ana_init
4699 : {
4700 : public:
4701 38 : impl_on_ana_init (std::vector<std::unique_ptr<state_machine>> &checkers,
4702 : known_function_manager &known_fn_mgr,
4703 : logger *logger)
4704 38 : : m_checkers (checkers),
4705 38 : m_known_fn_mgr (known_fn_mgr),
4706 38 : m_logger (logger)
4707 : {}
4708 :
4709 : void
4710 1 : register_state_machine (std::unique_ptr<state_machine> sm)
4711 : const final override
4712 : {
4713 1 : LOG_SCOPE (m_logger);
4714 1 : m_checkers.push_back (std::move (sm));
4715 1 : }
4716 :
4717 : void
4718 107 : register_known_function (const char *name,
4719 : std::unique_ptr<known_function> kf)
4720 : const final override
4721 : {
4722 107 : LOG_SCOPE (m_logger);
4723 107 : m_known_fn_mgr.add (name, std::move (kf));
4724 107 : }
4725 :
4726 : logger *
4727 39 : get_logger () const final override
4728 : {
4729 39 : return m_logger;
4730 : }
4731 :
4732 : private:
4733 : std::vector<std::unique_ptr<state_machine>> &m_checkers;
4734 : known_function_manager &m_known_fn_mgr;
4735 : logger *m_logger;
4736 : };
4737 :
4738 : static void
4739 13696 : maybe_dump_supergraph (const supergraph &sg, const char *name,
4740 : const dot_annotator *annotator = nullptr,
4741 : const exploded_graph *eg = nullptr)
4742 : {
4743 13696 : static int dump_idx = 0;
4744 13696 : if (!flag_dump_analyzer_supergraph)
4745 13676 : return;
4746 :
4747 20 : auto_timevar tv (TV_ANALYZER_DUMP);
4748 20 : std::string filename (dump_base_name);
4749 20 : filename += ".supergraph.";
4750 40 : filename += std::to_string (dump_idx++);
4751 20 : filename += ".";
4752 20 : filename += name;
4753 20 : filename += ".dot";
4754 20 : supergraph::dump_args_t args
4755 : ((enum supergraph_dot_flags)SUPERGRAPH_DOT_SHOW_BBS,
4756 : annotator,
4757 20 : eg);
4758 20 : sg.dump_dot (filename.c_str (), args);
4759 20 : }
4760 :
4761 : /* Run the analysis "engine". */
4762 :
4763 : void
4764 3423 : impl_run_checkers (logger *logger)
4765 : {
4766 3423 : LOG_SCOPE (logger);
4767 :
4768 3423 : if (logger)
4769 : {
4770 5 : logger->log ("BITS_BIG_ENDIAN: %i", BITS_BIG_ENDIAN ? 1 : 0);
4771 5 : logger->log ("BYTES_BIG_ENDIAN: %i", BYTES_BIG_ENDIAN ? 1 : 0);
4772 5 : logger->log ("WORDS_BIG_ENDIAN: %i", WORDS_BIG_ENDIAN ? 1 : 0);
4773 5 : log_stashed_constants (logger);
4774 : }
4775 :
4776 : /* If using LTO, ensure that the cgraph nodes have function bodies. */
4777 3423 : cgraph_node *node;
4778 13838 : FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
4779 10415 : node->get_untransformed_body ();
4780 :
4781 3423 : region_model_manager mgr;
4782 :
4783 : /* Create the supergraph. */
4784 3423 : supergraph sg (mgr, logger);
4785 :
4786 3423 : maybe_dump_supergraph (sg, "original");
4787 :
4788 3423 : sg.fixup_locations (logger);
4789 :
4790 3423 : maybe_dump_supergraph (sg, "fixup-locations");
4791 :
4792 3423 : engine eng (mgr, &sg);
4793 :
4794 3423 : state_purge_map *purge_map = nullptr;
4795 3423 : if (flag_analyzer_state_purge)
4796 3412 : purge_map = new state_purge_map (sg, eng.get_model_manager (), logger);
4797 :
4798 3423 : if (flag_analyzer_simplify_supergraph)
4799 : {
4800 3423 : sg.simplify (logger);
4801 3423 : maybe_dump_supergraph (sg, "simplified");
4802 : }
4803 :
4804 3423 : sg.sort_nodes (logger);
4805 3423 : maybe_dump_supergraph (sg, "sorted");
4806 :
4807 3423 : if (flag_dump_analyzer_state_purge)
4808 : {
4809 4 : auto_timevar tv (TV_ANALYZER_DUMP);
4810 4 : state_purge_annotator a (purge_map);
4811 4 : char *filename = concat (dump_base_name, ".state-purge.dot", nullptr);
4812 4 : supergraph::dump_args_t args ((enum supergraph_dot_flags)0, &a, nullptr);
4813 4 : sg.dump_dot (filename, args);
4814 4 : free (filename);
4815 4 : }
4816 :
4817 3423 : auto checkers = make_checkers (logger);
4818 :
4819 3423 : register_known_functions (*eng.get_known_function_manager (),
4820 3423 : *eng.get_model_manager ());
4821 :
4822 3423 : if (auto channel
4823 3423 : = g->get_channels ().analyzer_events_channel.get_if_active ())
4824 38 : channel->publish (impl_on_ana_init (checkers,
4825 38 : *eng.get_known_function_manager (),
4826 38 : logger));
4827 :
4828 3423 : if (logger)
4829 : {
4830 5 : int i = 0;
4831 40 : for (auto &sm : checkers)
4832 35 : logger->log ("checkers[%i]: %s", ++i, sm->get_name ());
4833 : }
4834 :
4835 : /* Extrinsic state shared by nodes in the graph. */
4836 3423 : const extrinsic_state ext_state (std::move (checkers), &eng, logger);
4837 :
4838 3423 : const analysis_plan plan (sg, logger);
4839 :
4840 : /* The exploded graph. */
4841 3423 : exploded_graph eg (sg, logger, ext_state, purge_map, plan,
4842 3423 : analyzer_verbosity);
4843 :
4844 : /* Add entrypoints to the graph for externally-callable functions. */
4845 3423 : eg.build_initial_worklist ();
4846 :
4847 : /* Now process the worklist, exploring the <point, state> graph. */
4848 3423 : eg.process_worklist ();
4849 :
4850 3423 : if (warn_analyzer_infinite_loop)
4851 3423 : eg.detect_infinite_loops ();
4852 :
4853 3423 : if (flag_dump_analyzer_exploded_graph)
4854 : {
4855 4 : auto_timevar tv (TV_ANALYZER_DUMP);
4856 4 : char *filename
4857 4 : = concat (dump_base_name, ".eg.dot", nullptr);
4858 4 : exploded_graph::dump_args_t args (eg);
4859 4 : root_cluster c;
4860 4 : eg.dump_dot (filename, &c, args);
4861 4 : free (filename);
4862 4 : }
4863 :
4864 : /* Now emit any saved diagnostics. */
4865 3423 : eg.get_diagnostic_manager ().emit_saved_diagnostics (eg);
4866 :
4867 3423 : eg.dump_exploded_nodes ();
4868 :
4869 3423 : eg.log_stats ();
4870 :
4871 3423 : if (flag_dump_analyzer_supergraph)
4872 : {
4873 : /* Dump post-analysis form of supergraph. */
4874 4 : exploded_graph_annotator a (eg);
4875 4 : maybe_dump_supergraph (sg, "eg", &a, &eg);
4876 4 : }
4877 :
4878 3423 : if (flag_dump_analyzer_json)
4879 0 : dump_analyzer_json (sg, eg);
4880 :
4881 3423 : if (flag_dump_analyzer_untracked)
4882 23 : eng.get_model_manager ()->dump_untracked_regions ();
4883 :
4884 3423 : delete purge_map;
4885 :
4886 : /* Free up any dominance info that we may have created. */
4887 13838 : FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
4888 : {
4889 10415 : function *fun = node->get_fun ();
4890 10415 : free_dominance_info (fun, CDI_DOMINATORS);
4891 : }
4892 3423 : }
4893 :
4894 : /* Handle -fdump-analyzer and -fdump-analyzer-stderr. */
4895 : static FILE *dump_fout = nullptr;
4896 :
4897 : /* Track if we're responsible for closing dump_fout. */
4898 : static bool owns_dump_fout = false;
4899 :
4900 : /* If dumping is enabled, attempt to create dump_fout if it hasn't already
4901 : been opened. Return it. */
4902 :
4903 : FILE *
4904 6889 : get_or_create_any_logfile ()
4905 : {
4906 6889 : if (!dump_fout)
4907 : {
4908 6884 : if (flag_dump_analyzer_stderr)
4909 0 : dump_fout = stderr;
4910 6884 : else if (flag_dump_analyzer)
4911 : {
4912 5 : char *dump_filename = concat (dump_base_name, ".analyzer.txt", nullptr);
4913 5 : dump_fout = fopen (dump_filename, "w");
4914 5 : free (dump_filename);
4915 5 : if (dump_fout)
4916 5 : owns_dump_fout = true;
4917 : }
4918 : }
4919 6889 : return dump_fout;
4920 : }
4921 :
4922 : /* External entrypoint to the analysis "engine".
4923 : Set up any dumps, then call impl_run_checkers. */
4924 :
4925 : void
4926 3423 : run_checkers ()
4927 : {
4928 : /* Save input_location. */
4929 3423 : location_t saved_input_location = input_location;
4930 :
4931 3423 : {
4932 3423 : log_user the_logger (nullptr);
4933 3423 : get_or_create_any_logfile ();
4934 3423 : if (dump_fout)
4935 5 : the_logger.set_logger (new logger (dump_fout, 0, 0,
4936 5 : *global_dc->get_reference_printer ()));
4937 3423 : LOG_SCOPE (the_logger.get_logger ());
4938 :
4939 3423 : impl_run_checkers (the_logger.get_logger ());
4940 :
4941 : /* end of lifetime of the_logger (so that dump file is closed after the
4942 : various dtors run). */
4943 3423 : }
4944 :
4945 3423 : if (owns_dump_fout)
4946 : {
4947 5 : fclose (dump_fout);
4948 5 : owns_dump_fout = false;
4949 5 : dump_fout = nullptr;
4950 : }
4951 :
4952 : /* Restore input_location. Subsequent passes may assume that input_location
4953 : is some arbitrary value *not* in the block tree, which might be violated
4954 : if we didn't restore it. */
4955 3423 : input_location = saved_input_location;
4956 3423 : }
4957 :
4958 : } // namespace ana
4959 :
4960 : #endif /* #if ENABLE_ANALYZER */
|