1 /* The analysis "engine". 2 Copyright (C) 2019-2020 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 "config.h" 22 #include "system.h" 23 #include "coretypes.h" 24 #include "tree.h" 25 #include "fold-const.h" 26 #include "gcc-rich-location.h" 27 #include "alloc-pool.h" 28 #include "fibonacci_heap.h" 29 #include "shortest-paths.h" 30 #include "diagnostic-core.h" 31 #include "diagnostic-event-id.h" 32 #include "diagnostic-path.h" 33 #include "function.h" 34 #include "pretty-print.h" 35 #include "sbitmap.h" 36 #include "bitmap.h" 37 #include "tristate.h" 38 #include "ordered-hash-map.h" 39 #include "selftest.h" 40 #include "analyzer/analyzer.h" 41 #include "analyzer/analyzer-logging.h" 42 #include "analyzer/region-model.h" 43 #include "analyzer/constraint-manager.h" 44 #include "analyzer/sm.h" 45 #include "analyzer/pending-diagnostic.h" 46 #include "analyzer/diagnostic-manager.h" 47 #include "cfg.h" 48 #include "basic-block.h" 49 #include "gimple.h" 50 #include "gimple-iterator.h" 51 #include "gimple-pretty-print.h" 52 #include "cgraph.h" 53 #include "digraph.h" 54 #include "analyzer/supergraph.h" 55 #include "analyzer/call-string.h" 56 #include "analyzer/program-point.h" 57 #include "analyzer/program-state.h" 58 #include "analyzer/exploded-graph.h" 59 #include "analyzer/analysis-plan.h" 60 #include "analyzer/checker-path.h" 61 #include "analyzer/state-purge.h" 62 #include "analyzer/bar-chart.h" 63 64 /* For an overview, see gcc/doc/analyzer.texi. */ 65 66 #if ENABLE_ANALYZER 67 68 namespace ana { 69 70 static int readability_comparator (const void *p1, const void *p2); 71 72 /* class impl_region_model_context : public region_model_context. */ 73 74 impl_region_model_context:: 75 impl_region_model_context (exploded_graph &eg, 76 const exploded_node *enode_for_diag, 77 const program_state *old_state, 78 program_state *new_state, 79 state_change *change, 80 const gimple *stmt, 81 stmt_finder *stmt_finder) 82 : m_eg (&eg), m_logger (eg.get_logger ()), 83 m_enode_for_diag (enode_for_diag), 84 m_old_state (old_state), 85 m_new_state (new_state), 86 m_change (change), 87 m_stmt (stmt), 88 m_stmt_finder (stmt_finder), 89 m_ext_state (eg.get_ext_state ()) 90 { 91 } 92 93 impl_region_model_context:: 94 impl_region_model_context (program_state *state, 95 state_change *change, 96 const extrinsic_state &ext_state, 97 logger *logger) 98 : m_eg (NULL), m_logger (logger), m_enode_for_diag (NULL), 99 m_old_state (NULL), 100 m_new_state (state), 101 m_change (change), 102 m_stmt (NULL), 103 m_stmt_finder (NULL), 104 m_ext_state (ext_state) 105 { 106 } 107 108 void 109 impl_region_model_context::warn (pending_diagnostic *d) 110 { 111 LOG_FUNC (get_logger ()); 112 if (m_eg) 113 m_eg->get_diagnostic_manager ().add_diagnostic 114 (m_enode_for_diag, m_enode_for_diag->get_supernode (), 115 m_stmt, m_stmt_finder, d); 116 } 117 118 void 119 impl_region_model_context::remap_svalue_ids (const svalue_id_map &map) 120 { 121 m_new_state->remap_svalue_ids (map); 122 if (m_change) 123 m_change->remap_svalue_ids (map); 124 } 125 126 int 127 impl_region_model_context::on_svalue_purge (svalue_id first_unused_sid, 128 const svalue_id_map &map) 129 { 130 int total = 0; 131 int sm_idx; 132 sm_state_map *smap; 133 FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap) 134 { 135 const state_machine &sm = m_ext_state.get_sm (sm_idx); 136 total += smap->on_svalue_purge (sm, sm_idx, first_unused_sid, 137 map, this); 138 } 139 if (m_change) 140 total += m_change->on_svalue_purge (first_unused_sid); 141 return total; 142 } 143 144 void 145 impl_region_model_context::on_unknown_change (svalue_id sid) 146 { 147 int sm_idx; 148 sm_state_map *smap; 149 FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap) 150 smap->on_unknown_change (sid); 151 } 152 153 /* class setjmp_svalue : public svalue. */ 154 155 /* Compare the fields of this setjmp_svalue with OTHER, returning true 156 if they are equal. 157 For use by svalue::operator==. */ 158 159 bool 160 setjmp_svalue::compare_fields (const setjmp_svalue &other) const 161 { 162 return m_setjmp_record == other.m_setjmp_record; 163 } 164 165 /* Implementation of svalue::add_to_hash vfunc for setjmp_svalue. */ 166 167 void 168 setjmp_svalue::add_to_hash (inchash::hash &hstate) const 169 { 170 hstate.add_int (m_setjmp_record.m_enode->m_index); 171 } 172 173 /* Get the index of the stored exploded_node. */ 174 175 int 176 setjmp_svalue::get_enode_index () const 177 { 178 return m_setjmp_record.m_enode->m_index; 179 } 180 181 /* Implementation of svalue::print_details vfunc for setjmp_svalue. */ 182 183 void 184 setjmp_svalue::print_details (const region_model &model ATTRIBUTE_UNUSED, 185 svalue_id this_sid ATTRIBUTE_UNUSED, 186 pretty_printer *pp) const 187 { 188 pp_printf (pp, "setjmp: EN: %i", get_enode_index ()); 189 } 190 191 /* Concrete implementation of sm_context, wiring it up to the rest of this 192 file. */ 193 194 class impl_sm_context : public sm_context 195 { 196 public: 197 impl_sm_context (exploded_graph &eg, 198 int sm_idx, 199 const state_machine &sm, 200 const exploded_node *enode_for_diag, 201 const program_state *old_state, 202 program_state *new_state, 203 state_change *change, 204 const sm_state_map *old_smap, 205 sm_state_map *new_smap, 206 stmt_finder *stmt_finder = NULL) 207 : sm_context (sm_idx, sm), 208 m_logger (eg.get_logger ()), 209 m_eg (eg), m_enode_for_diag (enode_for_diag), 210 m_old_state (old_state), m_new_state (new_state), 211 m_change (change), 212 m_old_smap (old_smap), m_new_smap (new_smap), 213 m_stmt_finder (stmt_finder) 214 { 215 } 216 217 logger *get_logger () const { return m_logger.get_logger (); } 218 219 tree get_fndecl_for_call (const gcall *call) FINAL OVERRIDE 220 { 221 impl_region_model_context old_ctxt 222 (m_eg, m_enode_for_diag, NULL, NULL/*m_enode->get_state ()*/, 223 m_change, call); 224 region_model *model = m_new_state->m_region_model; 225 return model->get_fndecl_for_call (call, &old_ctxt); 226 } 227 228 void on_transition (const supernode *node ATTRIBUTE_UNUSED, 229 const gimple *stmt ATTRIBUTE_UNUSED, 230 tree var, 231 state_machine::state_t from, 232 state_machine::state_t to, 233 tree origin) FINAL OVERRIDE 234 { 235 logger * const logger = get_logger (); 236 LOG_FUNC (logger); 237 impl_region_model_context old_ctxt 238 (m_eg, m_enode_for_diag, NULL, NULL/*m_enode->get_state ()*/, 239 m_change, stmt); 240 svalue_id var_old_sid 241 = m_old_state->m_region_model->get_rvalue (var, &old_ctxt); 242 243 impl_region_model_context new_ctxt (m_eg, m_enode_for_diag, 244 m_old_state, m_new_state, 245 m_change, NULL); 246 svalue_id var_new_sid 247 = m_new_state->m_region_model->get_rvalue (var, &new_ctxt); 248 svalue_id origin_new_sid 249 = m_new_state->m_region_model->get_rvalue (origin, &new_ctxt); 250 251 state_machine::state_t current = m_old_smap->get_state (var_old_sid); 252 if (current == from) 253 { 254 if (logger) 255 logger->log ("%s: state transition of %qE: %s -> %s", 256 m_sm.get_name (), 257 var, 258 m_sm.get_state_name (from), 259 m_sm.get_state_name (to)); 260 m_new_smap->set_state (m_new_state->m_region_model, var_new_sid, 261 to, origin_new_sid); 262 if (m_change) 263 m_change->add_sm_change (m_sm_idx, var_new_sid, from, to); 264 } 265 } 266 267 void warn_for_state (const supernode *snode, const gimple *stmt, 268 tree var, state_machine::state_t state, 269 pending_diagnostic *d) FINAL OVERRIDE 270 { 271 LOG_FUNC (get_logger ()); 272 gcc_assert (d); // take ownership 273 274 impl_region_model_context old_ctxt 275 (m_eg, m_enode_for_diag, m_old_state, m_new_state, m_change, NULL); 276 state_machine::state_t current; 277 if (var) 278 { 279 svalue_id var_old_sid 280 = m_old_state->m_region_model->get_rvalue (var, &old_ctxt); 281 current = m_old_smap->get_state (var_old_sid); 282 } 283 else 284 current = m_old_smap->get_global_state (); 285 286 if (state == current) 287 { 288 m_eg.get_diagnostic_manager ().add_diagnostic 289 (&m_sm, m_enode_for_diag, snode, stmt, m_stmt_finder, 290 var, state, d); 291 } 292 else 293 delete d; 294 } 295 296 /* Hook for picking more readable trees for SSA names of temporaries, 297 so that rather than e.g. 298 "double-free of '<unknown>'" 299 we can print: 300 "double-free of 'inbuf.data'". */ 301 302 tree get_readable_tree (tree expr) FINAL OVERRIDE 303 { 304 /* Only for SSA_NAMEs of temporaries; otherwise, return EXPR, as it's 305 likely to be the least surprising tree to report. */ 306 if (TREE_CODE (expr) != SSA_NAME) 307 return expr; 308 if (SSA_NAME_VAR (expr) != NULL) 309 return expr; 310 311 gcc_assert (m_new_state); 312 svalue_id sid = m_new_state->m_region_model->get_rvalue (expr, NULL); 313 /* Find trees for all regions storing the value. */ 314 auto_vec<path_var> pvs; 315 m_new_state->m_region_model->get_path_vars_for_svalue (sid, &pvs); 316 if (pvs.length () < 1) 317 return expr; 318 /* Pick the "best" such tree. */ 319 // TODO: should we also consider (and consolidate) equiv classes? 320 pvs.qsort (readability_comparator); 321 return pvs[0].m_tree; 322 } 323 324 state_machine::state_t get_global_state () const FINAL OVERRIDE 325 { 326 return m_old_state->m_checker_states[m_sm_idx]->get_global_state (); 327 } 328 329 void set_global_state (state_machine::state_t state) FINAL OVERRIDE 330 { 331 m_new_state->m_checker_states[m_sm_idx]->set_global_state (state); 332 } 333 334 void on_custom_transition (custom_transition *transition) FINAL OVERRIDE 335 { 336 transition->impl_transition (&m_eg, 337 const_cast<exploded_node *> (m_enode_for_diag), 338 m_sm_idx); 339 } 340 341 log_user m_logger; 342 exploded_graph &m_eg; 343 const exploded_node *m_enode_for_diag; 344 const program_state *m_old_state; 345 program_state *m_new_state; 346 state_change *m_change; 347 const sm_state_map *m_old_smap; 348 sm_state_map *m_new_smap; 349 stmt_finder *m_stmt_finder; 350 }; 351 352 /* Subclass of stmt_finder for finding the best stmt to report the leak at, 353 given the emission path. */ 354 355 class leak_stmt_finder : public stmt_finder 356 { 357 public: 358 leak_stmt_finder (const exploded_graph &eg, tree var) 359 : m_eg (eg), m_var (var) {} 360 361 stmt_finder *clone () const FINAL OVERRIDE 362 { 363 return new leak_stmt_finder (m_eg, m_var); 364 } 365 366 const gimple *find_stmt (const exploded_path &epath) 367 FINAL OVERRIDE 368 { 369 logger * const logger = m_eg.get_logger (); 370 LOG_FUNC (logger); 371 372 if (TREE_CODE (m_var) == SSA_NAME) 373 { 374 /* Locate the final write to this SSA name in the path. */ 375 const gimple *def_stmt = SSA_NAME_DEF_STMT (m_var); 376 377 int idx_of_def_stmt; 378 bool found = epath.find_stmt_backwards (def_stmt, &idx_of_def_stmt); 379 if (!found) 380 goto not_found; 381 382 /* What was the next write to the underlying var 383 after the SSA name was set? (if any). */ 384 385 for (unsigned idx = idx_of_def_stmt + 1; 386 idx < epath.m_edges.length (); 387 ++idx) 388 { 389 const exploded_edge *eedge = epath.m_edges[idx]; 390 if (logger) 391 logger->log ("eedge[%i]: EN %i -> EN %i", 392 idx, 393 eedge->m_src->m_index, 394 eedge->m_dest->m_index); 395 const exploded_node *dst_node = eedge->m_dest; 396 const program_point &dst_point = dst_node->get_point (); 397 const gimple *stmt = dst_point.get_stmt (); 398 if (!stmt) 399 continue; 400 if (const gassign *assign = dyn_cast <const gassign *> (stmt)) 401 { 402 tree lhs = gimple_assign_lhs (assign); 403 if (TREE_CODE (lhs) == SSA_NAME 404 && SSA_NAME_VAR (lhs) == SSA_NAME_VAR (m_var)) 405 return assign; 406 } 407 } 408 } 409 410 not_found: 411 412 /* Look backwards for the first statement with a location. */ 413 int i; 414 const exploded_edge *eedge; 415 FOR_EACH_VEC_ELT_REVERSE (epath.m_edges, i, eedge) 416 { 417 if (logger) 418 logger->log ("eedge[%i]: EN %i -> EN %i", 419 i, 420 eedge->m_src->m_index, 421 eedge->m_dest->m_index); 422 const exploded_node *dst_node = eedge->m_dest; 423 const program_point &dst_point = dst_node->get_point (); 424 const gimple *stmt = dst_point.get_stmt (); 425 if (stmt) 426 if (get_pure_location (stmt->location) != UNKNOWN_LOCATION) 427 return stmt; 428 } 429 430 gcc_unreachable (); 431 return NULL; 432 } 433 434 private: 435 const exploded_graph &m_eg; 436 tree m_var; 437 }; 438 439 /* A measurement of how good EXPR is for presenting to the user, so 440 that e.g. we can say prefer printing 441 "leak of 'tmp.m_ptr'" 442 over: 443 "leak of '<unknown>'". */ 444 445 static int 446 readability (const_tree expr) 447 { 448 gcc_assert (expr); 449 switch (TREE_CODE (expr)) 450 { 451 case COMPONENT_REF: 452 case MEM_REF: 453 /* Impose a slight readability penalty relative to that of 454 operand 0. */ 455 return readability (TREE_OPERAND (expr, 0)) - 1; 456 457 case SSA_NAME: 458 { 459 if (tree var = SSA_NAME_VAR (expr)) 460 return readability (var); 461 /* Avoid printing '<unknown>' for SSA names for temporaries. */ 462 return -1; 463 } 464 break; 465 466 case VAR_DECL: 467 /* Arbitrarily-chosen "high readability" value. */ 468 return 256; 469 470 default: 471 return 0; 472 } 473 474 return 0; 475 } 476 477 /* A qsort comparator for trees to sort them into most user-readable to 478 least user-readable. */ 479 480 static int 481 readability_comparator (const void *p1, const void *p2) 482 { 483 path_var pv1 = *(path_var const *)p1; 484 path_var pv2 = *(path_var const *)p2; 485 486 /* TODO: should we consider stack depths? */ 487 int r1 = readability (pv1.m_tree); 488 int r2 = readability (pv2.m_tree); 489 490 return r2 - r1; 491 } 492 493 /* Create an sm_context and use it to call SM's on_leak vfunc, so that 494 it can potentially complain about a leak of DST_SID (in a new region_model) 495 in the given STATE, where MAP can be used to map SID back to an "old" 496 region_model. */ 497 498 void 499 impl_region_model_context::on_state_leak (const state_machine &sm, 500 int sm_idx, 501 svalue_id dst_sid, 502 svalue_id first_unused_sid, 503 const svalue_id_map &map, 504 state_machine::state_t state) 505 { 506 logger * const logger = get_logger (); 507 LOG_SCOPE (logger); 508 if (logger) 509 logger->log ("considering leak of sv%i", dst_sid.as_int ()); 510 511 if (!m_eg) 512 return; 513 514 /* m_old_state also needs to be non-NULL so that the sm_ctxt can look 515 up the old state of the sid. */ 516 gcc_assert (m_old_state); 517 518 /* Don't report on sid leaking if it's equal to one of the used sids. 519 For example, given: 520 some_non_trivial_expression = malloc (sizeof (struct foo)); 521 we have: 522 _1 = malloc; (void *) 523 some_non_trivial_expression = _1; (struct foo *) 524 and at leak-detection time we may have: 525 sv5: {type: 'struct foo *', &r3} (used) 526 sv6: {type: 'void *', &r3} (unused) 527 where both point to the same region. We don't want to report a 528 leak of sv6, so we reject the report due to its equality with sv5. */ 529 gcc_assert (m_new_state); 530 gcc_assert (!first_unused_sid.null_p ()); 531 for (int i = 0; i < first_unused_sid.as_int (); i++) 532 { 533 svalue_id used_sid = svalue_id::from_int (i); 534 535 /* Use the "_without_cm" form of eval_condition, since 536 we're half-way through purging - we don't want to introduce new 537 equivalence classes into the constraint_manager for "sid" and 538 for each of the used_sids. */ 539 const region_model &rm = *m_new_state->m_region_model; 540 tristate eq = rm.eval_condition_without_cm (dst_sid, EQ_EXPR, used_sid); 541 if (eq.is_true ()) 542 { 543 if (logger) 544 logger->log ("rejecting leak of sv%i due to equality with sv%i", 545 dst_sid.as_int (), used_sid.as_int ()); 546 return; 547 } 548 } 549 550 /* SID has leaked within the new state: no regions use it. 551 We need to convert it back to a tree, but since no regions use it, we 552 have to use MAP to convert it back to an svalue_id within the old state. 553 We can then look that svalue_id up to locate regions and thus tree(s) 554 that use it. */ 555 556 svalue_id old_sid = map.get_src_for_dst (dst_sid); 557 558 auto_vec<path_var> leaked_pvs; 559 m_old_state->m_region_model->get_path_vars_for_svalue (old_sid, &leaked_pvs); 560 561 if (leaked_pvs.length () < 1) 562 return; 563 564 /* Find "best" leaked tree. 565 Sort the leaks into most human-readable first, through 566 to least user-readable. Given that we only emit one 567 leak per EC, this ought to ensure that we pick the most 568 user-readable description of each leaking EC. 569 This assumes that all vars in the EC have the same state. */ 570 leaked_pvs.qsort (readability_comparator); 571 572 tree leaked_tree = leaked_pvs[0].m_tree; 573 if (logger) 574 logger->log ("best leaked_tree: %qE", leaked_tree); 575 576 leak_stmt_finder stmt_finder (*m_eg, leaked_tree); 577 impl_sm_context sm_ctxt (*m_eg, sm_idx, sm, m_enode_for_diag, 578 m_old_state, m_new_state, 579 m_change, 580 m_old_state->m_checker_states[sm_idx], 581 m_new_state->m_checker_states[sm_idx], 582 &stmt_finder); 583 gcc_assert (m_enode_for_diag); 584 585 /* Don't complain about leaks when returning from "main". */ 586 if (m_enode_for_diag->get_supernode () 587 && m_enode_for_diag->get_supernode ()->return_p ()) 588 { 589 tree fndecl = m_enode_for_diag->get_function ()->decl; 590 if (0 == strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), "main")) 591 { 592 if (logger) 593 logger->log ("not reporting leak from main"); 594 return; 595 } 596 } 597 598 pending_diagnostic *pd = sm.on_leak (leaked_tree); 599 if (pd) 600 m_eg->get_diagnostic_manager ().add_diagnostic 601 (&sm, m_enode_for_diag, m_enode_for_diag->get_supernode (), 602 m_stmt, &stmt_finder, 603 leaked_tree, state, pd); 604 } 605 606 /* Implementation of region_model_context::on_inherited_svalue vfunc 607 for impl_region_model_context. 608 Notify all checkers that CHILD_SID has been created from PARENT_SID, 609 so that those state machines that inherit state can propagate the state 610 from parent to child. */ 611 612 void 613 impl_region_model_context::on_inherited_svalue (svalue_id parent_sid, 614 svalue_id child_sid) 615 { 616 if (!m_new_state) 617 return; 618 619 int sm_idx; 620 sm_state_map *smap; 621 FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap) 622 { 623 const state_machine &sm = m_ext_state.get_sm (sm_idx); 624 if (sm.inherited_state_p ()) 625 smap->on_inherited_svalue (parent_sid, child_sid); 626 } 627 } 628 629 /* Implementation of region_model_context::on_cast vfunc 630 for impl_region_model_context. 631 Notify all checkers that DST_SID is a cast of SRC_SID, so that sm-state 632 can be propagated from src to dst. */ 633 634 void 635 impl_region_model_context::on_cast (svalue_id src_sid, 636 svalue_id dst_sid) 637 { 638 if (!m_new_state) 639 return; 640 641 int sm_idx; 642 sm_state_map *smap; 643 FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap) 644 smap->on_cast (src_sid, dst_sid); 645 } 646 647 /* Implementation of region_model_context::on_condition vfunc. 648 Notify all state machines about the condition, which could lead to 649 state transitions. */ 650 651 void 652 impl_region_model_context::on_condition (tree lhs, enum tree_code op, tree rhs) 653 { 654 int sm_idx; 655 sm_state_map *smap; 656 FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap) 657 { 658 const state_machine &sm = m_ext_state.get_sm (sm_idx); 659 impl_sm_context sm_ctxt (*m_eg, sm_idx, sm, m_enode_for_diag, 660 m_old_state, m_new_state, 661 m_change, 662 m_old_state->m_checker_states[sm_idx], 663 m_new_state->m_checker_states[sm_idx]); 664 sm.on_condition (&sm_ctxt, 665 m_enode_for_diag->get_supernode (), m_stmt, 666 lhs, op, rhs); 667 } 668 } 669 670 /* Implementation of region_model_context::on_phi vfunc. 671 Notify all state machines about the phi, which could lead to 672 state transitions. */ 673 674 void 675 impl_region_model_context::on_phi (const gphi *phi, tree rhs) 676 { 677 int sm_idx; 678 sm_state_map *smap; 679 FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap) 680 { 681 const state_machine &sm = m_ext_state.get_sm (sm_idx); 682 impl_sm_context sm_ctxt (*m_eg, sm_idx, sm, m_enode_for_diag, 683 m_old_state, m_new_state, 684 m_change, 685 m_old_state->m_checker_states[sm_idx], 686 m_new_state->m_checker_states[sm_idx]); 687 sm.on_phi (&sm_ctxt, m_enode_for_diag->get_supernode (), phi, rhs); 688 } 689 } 690 691 /* Implementation of region_model_context::on_unexpected_tree_code vfunc. 692 Mark the new state as being invalid for further exploration. 693 TODO(stage1): introduce a warning for when this occurs. */ 694 695 void 696 impl_region_model_context::on_unexpected_tree_code (tree t, 697 const dump_location_t &loc) 698 { 699 logger * const logger = get_logger (); 700 if (logger) 701 logger->log ("unhandled tree code: %qs in %qs at %s:%i", 702 t ? get_tree_code_name (TREE_CODE (t)) : "(null)", 703 loc.get_impl_location ().m_function, 704 loc.get_impl_location ().m_file, 705 loc.get_impl_location ().m_line); 706 if (m_new_state) 707 m_new_state->m_valid = false; 708 } 709 710 /* struct point_and_state. */ 711 712 /* Assert that this object is sane. */ 713 714 void 715 point_and_state::validate (const extrinsic_state &ext_state) const 716 { 717 /* Skip this in a release build. */ 718 #if !CHECKING_P 719 return; 720 #endif 721 722 m_point.validate (); 723 724 m_state.validate (ext_state); 725 726 /* Verify that the callstring's model of the stack corresponds to that 727 of the region_model. */ 728 /* They should have the same depth. */ 729 gcc_assert (m_point.get_stack_depth () 730 == m_state.m_region_model->get_stack_depth ()); 731 /* Check the functions in the callstring vs those in the frames 732 at each depth. */ 733 for (int depth = 0; depth < m_point.get_stack_depth (); ++depth) 734 { 735 gcc_assert (m_point.get_function_at_depth (depth) 736 == m_state.m_region_model->get_function_at_depth (depth)); 737 } 738 } 739 740 /* Subroutine of print_enode_indices: print a run of indices from START_IDX 741 to END_IDX to PP, using and updating *FIRST_RUN. */ 742 743 static void 744 print_run (pretty_printer *pp, int start_idx, int end_idx, 745 bool *first_run) 746 { 747 if (!(*first_run)) 748 pp_string (pp, ", "); 749 *first_run = false; 750 if (start_idx == end_idx) 751 pp_printf (pp, "EN: %i", start_idx); 752 else 753 pp_printf (pp, "EN: %i-%i", start_idx, end_idx); 754 } 755 756 /* Print the indices within ENODES to PP, collecting them as 757 runs/singletons e.g. "EN: 4-7, EN: 20-23, EN: 42". */ 758 759 static void 760 print_enode_indices (pretty_printer *pp, 761 const auto_vec<exploded_node *> &enodes) 762 { 763 int cur_start_idx = -1; 764 int cur_finish_idx = -1; 765 bool first_run = true; 766 unsigned i; 767 exploded_node *enode; 768 FOR_EACH_VEC_ELT (enodes, i, enode) 769 { 770 if (cur_start_idx == -1) 771 { 772 gcc_assert (cur_finish_idx == -1); 773 cur_start_idx = cur_finish_idx = enode->m_index; 774 } 775 else 776 { 777 if (enode->m_index == cur_finish_idx + 1) 778 /* Continuation of a run. */ 779 cur_finish_idx = enode->m_index; 780 else 781 { 782 /* Finish existing run, start a new one. */ 783 gcc_assert (cur_start_idx >= 0); 784 gcc_assert (cur_finish_idx >= 0); 785 print_run (pp, cur_start_idx, cur_finish_idx, 786 &first_run); 787 cur_start_idx = cur_finish_idx = enode->m_index; 788 } 789 } 790 } 791 /* Finish any existing run. */ 792 if (cur_start_idx >= 0) 793 { 794 gcc_assert (cur_finish_idx >= 0); 795 print_run (pp, cur_start_idx, cur_finish_idx, 796 &first_run); 797 } 798 } 799 800 /* class exploded_node : public dnode<eg_traits>. */ 801 802 /* exploded_node's ctor. */ 803 804 exploded_node::exploded_node (const point_and_state &ps, 805 int index) 806 : m_ps (ps), m_status (STATUS_WORKLIST), m_index (index) 807 { 808 gcc_checking_assert (ps.get_state ().m_region_model->canonicalized_p ()); 809 } 810 811 /* For use by dump_dot, get a value for the .dot "fillcolor" attribute. 812 Colorize by sm-state, to make it easier to see how sm-state propagates 813 through the exploded_graph. */ 814 815 const char * 816 exploded_node::get_dot_fillcolor () const 817 { 818 const program_state &state = get_state (); 819 820 /* We want to be able to easily distinguish the no-sm-state case, 821 and to be able to distinguish cases where there's a single state 822 from each other. 823 824 Sum the sm_states, and use the result to choose from a table, 825 modulo table-size, special-casing the "no sm-state" case. */ 826 int total_sm_state = 0; 827 int i; 828 sm_state_map *smap; 829 FOR_EACH_VEC_ELT (state.m_checker_states, i, smap) 830 { 831 for (sm_state_map::iterator_t iter = smap->begin (); 832 iter != smap->end (); 833 ++iter) 834 total_sm_state += (*iter).second.m_state; 835 total_sm_state += smap->get_global_state (); 836 } 837 838 if (total_sm_state > 0) 839 { 840 /* An arbitrarily-picked collection of light colors. */ 841 const char * const colors[] 842 = {"azure", "coral", "cornsilk", "lightblue", "yellow"}; 843 const int num_colors = sizeof (colors) / sizeof (colors[0]); 844 return colors[total_sm_state % num_colors]; 845 } 846 else 847 /* No sm-state. */ 848 return "lightgrey"; 849 } 850 851 /* Implementation of dnode::dump_dot vfunc for exploded_node. */ 852 853 void 854 exploded_node::dump_dot (graphviz_out *gv, const dump_args_t &args) const 855 { 856 pretty_printer *pp = gv->get_pp (); 857 858 dump_dot_id (pp); 859 pp_printf (pp, " [shape=none,margin=0,style=filled,fillcolor=%s,label=\"", 860 get_dot_fillcolor ()); 861 pp_write_text_to_stream (pp); 862 863 pp_printf (pp, "EN: %i", m_index); 864 if (m_status == STATUS_MERGER) 865 pp_string (pp, " (merger)"); 866 pp_newline (pp); 867 868 format f (true); 869 m_ps.get_point ().print (pp, f); 870 pp_newline (pp); 871 872 const extrinsic_state &ext_state = args.m_eg.get_ext_state (); 873 const program_state &state = m_ps.get_state (); 874 state.dump_to_pp (ext_state, true, pp); 875 pp_newline (pp); 876 877 { 878 int i; 879 sm_state_map *smap; 880 FOR_EACH_VEC_ELT (state.m_checker_states, i, smap) 881 { 882 if (!smap->is_empty_p ()) 883 { 884 pp_printf (pp, "%s: ", ext_state.get_name (i)); 885 smap->print (ext_state.get_sm (i), state.m_region_model, pp); 886 pp_newline (pp); 887 } 888 } 889 } 890 891 /* Dump any saved_diagnostics at this enode. */ 892 { 893 const diagnostic_manager &dm = args.m_eg.get_diagnostic_manager (); 894 for (unsigned i = 0; i < dm.get_num_diagnostics (); i++) 895 { 896 const saved_diagnostic *sd = dm.get_saved_diagnostic (i); 897 if (sd->m_enode == this) 898 { 899 pp_printf (pp, "DIAGNOSTIC: %s", sd->m_d->get_kind ()); 900 pp_newline (pp); 901 } 902 } 903 } 904 905 pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true); 906 907 pp_string (pp, "\"];\n\n"); 908 pp_flush (pp); 909 } 910 911 /* Dump this to PP in a form suitable for use as an id in .dot output. */ 912 913 void 914 exploded_node::dump_dot_id (pretty_printer *pp) const 915 { 916 pp_printf (pp, "exploded_node_%i", m_index); 917 } 918 919 /* Dump a multiline representation of this node to PP. */ 920 921 void 922 exploded_node::dump_to_pp (pretty_printer *pp, 923 const extrinsic_state &ext_state) const 924 { 925 pp_printf (pp, "EN: %i", m_index); 926 pp_newline (pp); 927 928 format f (true); 929 m_ps.get_point ().print (pp, f); 930 pp_newline (pp); 931 932 m_ps.get_state ().dump_to_pp (ext_state, false, pp); 933 pp_newline (pp); 934 } 935 936 /* Dump a multiline representation of this node to FILE. */ 937 938 void 939 exploded_node::dump (FILE *fp, 940 const extrinsic_state &ext_state) const 941 { 942 pretty_printer pp; 943 pp_format_decoder (&pp) = default_tree_printer; 944 pp_show_color (&pp) = pp_show_color (global_dc->printer); 945 pp.buffer->stream = fp; 946 dump_to_pp (&pp, ext_state); 947 pp_flush (&pp); 948 } 949 950 /* Dump a multiline representation of this node to stderr. */ 951 952 DEBUG_FUNCTION void 953 exploded_node::dump (const extrinsic_state &ext_state) const 954 { 955 dump (stderr, ext_state); 956 } 957 958 } // namespace ana 959 960 /* Return true if FNDECL has a gimple body. */ 961 // TODO: is there a pre-canned way to do this? 962 963 bool 964 fndecl_has_gimple_body_p (tree fndecl) 965 { 966 if (fndecl == NULL_TREE) 967 return false; 968 969 cgraph_node *n = cgraph_node::get (fndecl); 970 if (!n) 971 return false; 972 973 return n->has_gimple_body_p (); 974 } 975 976 namespace ana { 977 978 /* A pending_diagnostic subclass for implementing "__analyzer_dump_path". */ 979 980 class dump_path_diagnostic 981 : public pending_diagnostic_subclass<dump_path_diagnostic> 982 { 983 public: 984 bool emit (rich_location *richloc) FINAL OVERRIDE 985 { 986 inform (richloc, "path"); 987 return true; 988 } 989 990 const char *get_kind () const FINAL OVERRIDE { return "dump_path_diagnostic"; } 991 992 bool operator== (const dump_path_diagnostic &) const 993 { 994 return true; 995 } 996 }; 997 998 /* Modify STATE in place, applying the effects of the stmt at this node's 999 point. */ 1000 1001 exploded_node::on_stmt_flags 1002 exploded_node::on_stmt (exploded_graph &eg, 1003 const supernode *snode, 1004 const gimple *stmt, 1005 program_state *state, 1006 state_change *change) const 1007 { 1008 /* Preserve the old state. It is used here for looking 1009 up old checker states, for determining state transitions, and 1010 also within impl_region_model_context and impl_sm_context for 1011 going from tree to svalue_id. */ 1012 const program_state old_state (*state); 1013 1014 impl_region_model_context ctxt (eg, this, 1015 &old_state, state, change, 1016 stmt); 1017 1018 if (const gassign *assign = dyn_cast <const gassign *> (stmt)) 1019 state->m_region_model->on_assignment (assign, &ctxt); 1020 1021 if (const greturn *return_ = dyn_cast <const greturn *> (stmt)) 1022 state->m_region_model->on_return (return_, &ctxt); 1023 1024 /* Track whether we have a gcall to a function that's not recognized by 1025 anything, for which we don't have a function body, or for which we 1026 don't know the fndecl. */ 1027 bool unknown_side_effects = false; 1028 if (const gcall *call = dyn_cast <const gcall *> (stmt)) 1029 { 1030 /* Debugging/test support. */ 1031 if (is_special_named_call_p (call, "__analyzer_dump", 0)) 1032 { 1033 /* Handle the builtin "__analyzer_dump" by dumping state 1034 to stderr. */ 1035 dump (eg.get_ext_state ()); 1036 } 1037 else if (is_special_named_call_p (call, "__analyzer_dump_path", 0)) 1038 { 1039 /* Handle the builtin "__analyzer_dump_path" by queuing a 1040 diagnostic at this exploded_node. */ 1041 ctxt.warn (new dump_path_diagnostic ()); 1042 } 1043 else if (is_special_named_call_p (call, "__analyzer_dump_region_model", 0)) 1044 { 1045 /* Handle the builtin "__analyzer_dump_region_model" by dumping 1046 the region model's state to stderr. */ 1047 state->m_region_model->dump (false); 1048 } 1049 else if (is_special_named_call_p (call, "__analyzer_eval", 1)) 1050 { 1051 /* Handle the builtin "__analyzer_eval" by evaluating the input 1052 and dumping as a dummy warning, so that test cases can use 1053 dg-warning to validate the result (and so unexpected warnings will 1054 lead to DejaGnu failures). */ 1055 tree t_arg = gimple_call_arg (call, 0); 1056 tristate t 1057 = state->m_region_model->eval_condition (t_arg, 1058 NE_EXPR, 1059 integer_zero_node, 1060 &ctxt); 1061 warning_at (call->location, 0, "%s", t.as_string ()); 1062 } 1063 else if (is_special_named_call_p (call, "__analyzer_break", 0)) 1064 { 1065 /* Handle the builtin "__analyzer_break" by triggering a 1066 breakpoint. */ 1067 /* TODO: is there a good cross-platform way to do this? */ 1068 raise (SIGINT); 1069 } 1070 else if (is_special_named_call_p (call, "__analyzer_dump_exploded_nodes", 1071 1)) 1072 { 1073 /* This is handled elsewhere. */ 1074 } 1075 else if (is_setjmp_call_p (call)) 1076 state->m_region_model->on_setjmp (call, this, &ctxt); 1077 else if (is_longjmp_call_p (call)) 1078 { 1079 on_longjmp (eg, call, state, &ctxt); 1080 return on_stmt_flags::terminate_path (); 1081 } 1082 else 1083 unknown_side_effects = state->m_region_model->on_call_pre (call, &ctxt); 1084 } 1085 1086 bool any_sm_changes = false; 1087 int sm_idx; 1088 sm_state_map *smap; 1089 FOR_EACH_VEC_ELT (old_state.m_checker_states, sm_idx, smap) 1090 { 1091 const state_machine &sm = eg.get_ext_state ().get_sm (sm_idx); 1092 const sm_state_map *old_smap 1093 = old_state.m_checker_states[sm_idx]; 1094 sm_state_map *new_smap = state->m_checker_states[sm_idx]; 1095 impl_sm_context sm_ctxt (eg, sm_idx, sm, this, &old_state, state, 1096 change, 1097 old_smap, new_smap); 1098 /* Allow the state_machine to handle the stmt. */ 1099 if (sm.on_stmt (&sm_ctxt, snode, stmt)) 1100 unknown_side_effects = false; 1101 else 1102 { 1103 /* For those stmts that were not handled by the state machine. */ 1104 if (const gcall *call = dyn_cast <const gcall *> (stmt)) 1105 { 1106 tree callee_fndecl 1107 = state->m_region_model->get_fndecl_for_call (call, &ctxt); 1108 1109 if (!fndecl_has_gimple_body_p (callee_fndecl)) 1110 new_smap->purge_for_unknown_fncall (eg, sm, call, callee_fndecl, 1111 state->m_region_model, 1112 &ctxt); 1113 } 1114 } 1115 if (*old_smap != *new_smap) 1116 any_sm_changes = true; 1117 } 1118 1119 if (const gcall *call = dyn_cast <const gcall *> (stmt)) 1120 state->m_region_model->on_call_post (call, unknown_side_effects, &ctxt); 1121 1122 return on_stmt_flags (any_sm_changes); 1123 } 1124 1125 /* Consider the effect of following superedge SUCC from this node. 1126 1127 Return true if it's feasible to follow the edge, or false 1128 if it's infeasible. 1129 1130 Examples: if it's the "true" branch within 1131 a CFG and we know the conditional is false, we know it's infeasible. 1132 If it's one of multiple interprocedual "return" edges, then only 1133 the edge back to the most recent callsite is feasible. 1134 1135 Update NEXT_STATE accordingly (e.g. to record that a condition was 1136 true or false, or that the NULL-ness of a pointer has been checked, 1137 pushing/popping stack frames, etc). 1138 1139 Update NEXT_POINT accordingly (updating the call string). */ 1140 1141 bool 1142 exploded_node::on_edge (exploded_graph &eg, 1143 const superedge *succ, 1144 program_point *next_point, 1145 program_state *next_state, 1146 state_change *change) const 1147 { 1148 LOG_FUNC (eg.get_logger ()); 1149 1150 if (!next_point->on_edge (eg, succ)) 1151 return false; 1152 1153 if (!next_state->on_edge (eg, *this, succ, change)) 1154 return false; 1155 1156 return true; 1157 } 1158 1159 /* Verify that the stack at LONGJMP_POINT is still valid, given a call 1160 to "setjmp" at SETJMP_POINT - the stack frame that "setjmp" was 1161 called in must still be valid. 1162 1163 Caveat: this merely checks the call_strings in the points; it doesn't 1164 detect the case where a frame returns and is then called again. */ 1165 1166 static bool 1167 valid_longjmp_stack_p (const program_point &longjmp_point, 1168 const program_point &setjmp_point) 1169 { 1170 const call_string &cs_at_longjmp = longjmp_point.get_call_string (); 1171 const call_string &cs_at_setjmp = setjmp_point.get_call_string (); 1172 1173 if (cs_at_longjmp.length () < cs_at_setjmp.length ()) 1174 return false; 1175 1176 /* Check that the call strings match, up to the depth of the 1177 setjmp point. */ 1178 for (unsigned depth = 0; depth < cs_at_setjmp.length (); depth++) 1179 if (cs_at_longjmp[depth] != cs_at_setjmp[depth]) 1180 return false; 1181 1182 return true; 1183 } 1184 1185 /* A pending_diagnostic subclass for complaining about bad longjmps, 1186 where the enclosing function of the "setjmp" has returned (and thus 1187 the stack frame no longer exists). */ 1188 1189 class stale_jmp_buf : public pending_diagnostic_subclass<dump_path_diagnostic> 1190 { 1191 public: 1192 stale_jmp_buf (const gcall *setjmp_call, const gcall *longjmp_call) 1193 : m_setjmp_call (setjmp_call), m_longjmp_call (longjmp_call) 1194 {} 1195 1196 bool emit (rich_location *richloc) FINAL OVERRIDE 1197 { 1198 return warning_at 1199 (richloc, OPT_Wanalyzer_stale_setjmp_buffer, 1200 "%qs called after enclosing function of %qs has returned", 1201 get_user_facing_name (m_longjmp_call), 1202 get_user_facing_name (m_setjmp_call)); 1203 } 1204 1205 const char *get_kind () const FINAL OVERRIDE 1206 { return "stale_jmp_buf"; } 1207 1208 bool operator== (const stale_jmp_buf &other) const 1209 { 1210 return (m_setjmp_call == other.m_setjmp_call 1211 && m_longjmp_call == other.m_longjmp_call); 1212 } 1213 1214 private: 1215 const gcall *m_setjmp_call; 1216 const gcall *m_longjmp_call; 1217 }; 1218 1219 /* Handle LONGJMP_CALL, a call to longjmp or siglongjmp. 1220 1221 Attempt to locate where setjmp/sigsetjmp was called on the jmp_buf and build 1222 an exploded_node and exploded_edge to it representing a rewind to that frame, 1223 handling the various kinds of failure that can occur. */ 1224 1225 void 1226 exploded_node::on_longjmp (exploded_graph &eg, 1227 const gcall *longjmp_call, 1228 program_state *new_state, 1229 region_model_context *ctxt) const 1230 { 1231 tree buf_ptr = gimple_call_arg (longjmp_call, 0); 1232 1233 region_model *new_region_model = new_state->m_region_model; 1234 region_id buf_rid = new_region_model->deref_rvalue (buf_ptr, ctxt); 1235 region *buf = new_region_model->get_region (buf_rid); 1236 if (!buf) 1237 return; 1238 1239 svalue_id buf_content_sid 1240 = buf->get_value (*new_region_model, false, ctxt); 1241 svalue *buf_content_sval = new_region_model->get_svalue (buf_content_sid); 1242 if (!buf_content_sval) 1243 return; 1244 setjmp_svalue *setjmp_sval = buf_content_sval->dyn_cast_setjmp_svalue (); 1245 if (!setjmp_sval) 1246 return; 1247 1248 const setjmp_record tmp_setjmp_record = setjmp_sval->get_setjmp_record (); 1249 1250 /* Build a custom enode and eedge for rewinding from the longjmp/siglongjmp 1251 call back to the setjmp/sigsetjmp. */ 1252 rewind_info_t rewind_info (tmp_setjmp_record, longjmp_call); 1253 1254 const gcall *setjmp_call = rewind_info.get_setjmp_call (); 1255 const program_point &setjmp_point = rewind_info.get_setjmp_point (); 1256 1257 const program_point &longjmp_point = get_point (); 1258 1259 /* Verify that the setjmp's call_stack hasn't been popped. */ 1260 if (!valid_longjmp_stack_p (longjmp_point, setjmp_point)) 1261 { 1262 ctxt->warn (new stale_jmp_buf (setjmp_call, longjmp_call)); 1263 return; 1264 } 1265 1266 gcc_assert (longjmp_point.get_stack_depth () 1267 >= setjmp_point.get_stack_depth ()); 1268 1269 /* Update the state for use by the destination node. */ 1270 1271 /* Stash the current number of diagnostics so that we can update 1272 any that this adds to show where the longjmp is rewinding to. */ 1273 1274 diagnostic_manager *dm = &eg.get_diagnostic_manager (); 1275 unsigned prev_num_diagnostics = dm->get_num_diagnostics (); 1276 1277 new_region_model->on_longjmp (longjmp_call, setjmp_call, 1278 setjmp_point.get_stack_depth (), ctxt); 1279 1280 program_point next_point 1281 = program_point::after_supernode (setjmp_point.get_supernode (), 1282 setjmp_point.get_call_string ()); 1283 1284 state_change change; 1285 exploded_node *next = eg.get_or_create_node (next_point, *new_state, &change); 1286 1287 /* Create custom exploded_edge for a longjmp. */ 1288 if (next) 1289 { 1290 exploded_edge *eedge 1291 = eg.add_edge (const_cast<exploded_node *> (this), next, NULL, 1292 change, 1293 new rewind_info_t (tmp_setjmp_record, longjmp_call)); 1294 1295 /* For any diagnostics that were queued here (such as leaks) we want 1296 the checker_path to show the rewinding events after the "final event" 1297 so that the user sees where the longjmp is rewinding to (otherwise the 1298 path is meaningless). 1299 1300 For example, we want to emit something like: 1301 | NN | { 1302 | NN | longjmp (env, 1); 1303 | | ~~~~~~~~~~~~~~~~ 1304 | | | 1305 | | (10) 'ptr' leaks here; was allocated at (7) 1306 | | (11) rewinding from 'longjmp' in 'inner'... 1307 | 1308 <-------------+ 1309 | 1310 'outer': event 12 1311 | 1312 | NN | i = setjmp(env); 1313 | | ^~~~~~ 1314 | | | 1315 | | (12) ...to 'setjmp' in 'outer' (saved at (2)) 1316 1317 where the "final" event above is event (10), but we want to append 1318 events (11) and (12) afterwards. 1319 1320 Do this by setting m_trailing_eedge on any diagnostics that were 1321 just saved. */ 1322 unsigned num_diagnostics = dm->get_num_diagnostics (); 1323 for (unsigned i = prev_num_diagnostics; i < num_diagnostics; i++) 1324 { 1325 saved_diagnostic *sd = dm->get_saved_diagnostic (i); 1326 sd->m_trailing_eedge = eedge; 1327 } 1328 } 1329 } 1330 1331 /* Subroutine of exploded_graph::process_node for finding the successors 1332 of the supernode for a function exit basic block. 1333 1334 Ensure that pop_frame is called, potentially queuing diagnostics about 1335 leaks. */ 1336 1337 void 1338 exploded_node::detect_leaks (exploded_graph &eg) const 1339 { 1340 LOG_FUNC_1 (eg.get_logger (), "EN: %i", m_index); 1341 1342 gcc_assert (get_point ().get_supernode ()->return_p ()); 1343 1344 /* If we're not a "top-level" function, do nothing; pop_frame 1345 will be called when handling the return superedge. */ 1346 if (get_point ().get_stack_depth () > 1) 1347 return; 1348 1349 /* We have a "top-level" function. */ 1350 gcc_assert (get_point ().get_stack_depth () == 1); 1351 1352 const program_state &old_state = get_state (); 1353 1354 /* Work with a temporary copy of the state: pop the frame, and see 1355 what leaks (via purge_unused_svalues). */ 1356 program_state new_state (old_state); 1357 1358 gcc_assert (new_state.m_region_model); 1359 1360 purge_stats stats; 1361 impl_region_model_context ctxt (eg, this, 1362 &old_state, &new_state, 1363 NULL, 1364 get_stmt ()); 1365 new_state.m_region_model->pop_frame (region_id::null (), 1366 true, &stats, &ctxt); 1367 } 1368 1369 /* Dump the successors and predecessors of this enode to OUTF. */ 1370 1371 void 1372 exploded_node::dump_succs_and_preds (FILE *outf) const 1373 { 1374 unsigned i; 1375 exploded_edge *e; 1376 { 1377 auto_vec<exploded_node *> preds (m_preds.length ()); 1378 FOR_EACH_VEC_ELT (m_preds, i, e) 1379 preds.quick_push (e->m_src); 1380 pretty_printer pp; 1381 print_enode_indices (&pp, preds); 1382 fprintf (outf, "preds: %s\n", 1383 pp_formatted_text (&pp)); 1384 } 1385 { 1386 auto_vec<exploded_node *> succs (m_succs.length ()); 1387 FOR_EACH_VEC_ELT (m_succs, i, e) 1388 succs.quick_push (e->m_dest); 1389 pretty_printer pp; 1390 print_enode_indices (&pp, succs); 1391 fprintf (outf, "succs: %s\n", 1392 pp_formatted_text (&pp)); 1393 } 1394 } 1395 1396 /* class rewind_info_t : public exploded_edge::custom_info_t. */ 1397 1398 /* Implementation of exploded_edge::custom_info_t::update_model vfunc 1399 for rewind_info_t. 1400 1401 Update state for the special-case of a rewind of a longjmp 1402 to a setjmp (which doesn't have a superedge, but does affect 1403 state). */ 1404 1405 void 1406 rewind_info_t::update_model (region_model *model, 1407 const exploded_edge &eedge) 1408 { 1409 const program_point &longjmp_point = eedge.m_src->get_point (); 1410 const program_point &setjmp_point = eedge.m_dest->get_point (); 1411 1412 gcc_assert (longjmp_point.get_stack_depth () 1413 >= setjmp_point.get_stack_depth ()); 1414 1415 model->on_longjmp (get_longjmp_call (), 1416 get_setjmp_call (), 1417 setjmp_point.get_stack_depth (), NULL); 1418 } 1419 1420 /* Implementation of exploded_edge::custom_info_t::add_events_to_path vfunc 1421 for rewind_info_t. */ 1422 1423 void 1424 rewind_info_t::add_events_to_path (checker_path *emission_path, 1425 const exploded_edge &eedge) 1426 { 1427 const exploded_node *src_node = eedge.m_src; 1428 const program_point &src_point = src_node->get_point (); 1429 const int src_stack_depth = src_point.get_stack_depth (); 1430 const exploded_node *dst_node = eedge.m_dest; 1431 const program_point &dst_point = dst_node->get_point (); 1432 const int dst_stack_depth = dst_point.get_stack_depth (); 1433 1434 emission_path->add_event 1435 (new rewind_from_longjmp_event 1436 (&eedge, get_longjmp_call ()->location, 1437 src_point.get_fndecl (), 1438 src_stack_depth, this)); 1439 emission_path->add_event 1440 (new rewind_to_setjmp_event 1441 (&eedge, get_setjmp_call ()->location, 1442 dst_point.get_fndecl (), 1443 dst_stack_depth, this)); 1444 } 1445 1446 /* class exploded_edge : public dedge<eg_traits>. */ 1447 1448 /* exploded_edge's ctor. */ 1449 1450 exploded_edge::exploded_edge (exploded_node *src, exploded_node *dest, 1451 const extrinsic_state &ext_state, 1452 const superedge *sedge, 1453 const state_change &change, 1454 custom_info_t *custom_info) 1455 : dedge<eg_traits> (src, dest), m_sedge (sedge), m_change (change), 1456 m_custom_info (custom_info) 1457 { 1458 change.validate (dest->get_state (), ext_state); 1459 } 1460 1461 /* exploded_edge's dtor. */ 1462 1463 exploded_edge::~exploded_edge () 1464 { 1465 delete m_custom_info; 1466 } 1467 1468 /* Implementation of dedge::dump_dot vfunc for exploded_edge. 1469 Use the label of the underlying superedge, if any. */ 1470 1471 void 1472 exploded_edge::dump_dot (graphviz_out *gv, const dump_args_t &args) const 1473 { 1474 pretty_printer *pp = gv->get_pp (); 1475 1476 const char *style = "\"solid,bold\""; 1477 const char *color = "black"; 1478 int weight = 10; 1479 const char *constraint = "true"; 1480 1481 if (m_sedge) 1482 switch (m_sedge->m_kind) 1483 { 1484 default: 1485 gcc_unreachable (); 1486 case SUPEREDGE_CFG_EDGE: 1487 break; 1488 case SUPEREDGE_CALL: 1489 color = "red"; 1490 //constraint = "false"; 1491 break; 1492 case SUPEREDGE_RETURN: 1493 color = "green"; 1494 //constraint = "false"; 1495 break; 1496 case SUPEREDGE_INTRAPROCEDURAL_CALL: 1497 style = "\"dotted\""; 1498 break; 1499 } 1500 if (m_custom_info) 1501 { 1502 color = "red"; 1503 style = "\"dotted\""; 1504 } 1505 1506 m_src->dump_dot_id (pp); 1507 pp_string (pp, " -> "); 1508 m_dest->dump_dot_id (pp); 1509 pp_printf (pp, 1510 (" [style=%s, color=%s, weight=%d, constraint=%s," 1511 " headlabel=\""), 1512 style, color, weight, constraint); 1513 1514 if (m_sedge) 1515 m_sedge->dump_label_to_pp (pp, false); 1516 else if (m_custom_info) 1517 m_custom_info->print (pp); 1518 1519 m_change.dump (pp, args.m_eg.get_ext_state ()); 1520 //pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false); 1521 1522 pp_printf (pp, "\"];\n"); 1523 } 1524 1525 /* struct stats. */ 1526 1527 /* stats' ctor. */ 1528 1529 stats::stats (int num_supernodes) 1530 : m_node_reuse_count (0), 1531 m_node_reuse_after_merge_count (0), 1532 m_num_supernodes (num_supernodes) 1533 { 1534 for (int i = 0; i < NUM_POINT_KINDS; i++) 1535 m_num_nodes[i] = 0; 1536 } 1537 1538 /* Log these stats in multiline form to LOGGER. */ 1539 1540 void 1541 stats::log (logger *logger) const 1542 { 1543 gcc_assert (logger); 1544 for (int i = 0; i < NUM_POINT_KINDS; i++) 1545 if (m_num_nodes[i] > 0) 1546 logger->log ("m_num_nodes[%s]: %i", 1547 point_kind_to_string (static_cast <enum point_kind> (i)), 1548 m_num_nodes[i]); 1549 logger->log ("m_node_reuse_count: %i", m_node_reuse_count); 1550 logger->log ("m_node_reuse_after_merge_count: %i", 1551 m_node_reuse_after_merge_count); 1552 } 1553 1554 /* Dump these stats in multiline form to OUT. */ 1555 1556 void 1557 stats::dump (FILE *out) const 1558 { 1559 for (int i = 0; i < NUM_POINT_KINDS; i++) 1560 if (m_num_nodes[i] > 0) 1561 fprintf (out, "m_num_nodes[%s]: %i\n", 1562 point_kind_to_string (static_cast <enum point_kind> (i)), 1563 m_num_nodes[i]); 1564 fprintf (out, "m_node_reuse_count: %i\n", m_node_reuse_count); 1565 fprintf (out, "m_node_reuse_after_merge_count: %i\n", 1566 m_node_reuse_after_merge_count); 1567 1568 if (m_num_supernodes > 0) 1569 fprintf (out, "PK_AFTER_SUPERNODE nodes per supernode: %.2f\n", 1570 (float)m_num_nodes[PK_AFTER_SUPERNODE] / (float)m_num_supernodes); 1571 } 1572 1573 /* Return the total number of enodes recorded within this object. */ 1574 1575 int 1576 stats::get_total_enodes () const 1577 { 1578 int result = 0; 1579 for (int i = 0; i < NUM_POINT_KINDS; i++) 1580 result += m_num_nodes[i]; 1581 return result; 1582 } 1583 1584 /* strongly_connected_components's ctor. Tarjan's SCC algorithm. */ 1585 1586 strongly_connected_components:: 1587 strongly_connected_components (const supergraph &sg, logger *logger) 1588 : m_sg (sg), m_per_node (m_sg.num_nodes ()) 1589 { 1590 LOG_SCOPE (logger); 1591 auto_timevar tv (TV_ANALYZER_SCC); 1592 1593 for (int i = 0; i < m_sg.num_nodes (); i++) 1594 m_per_node.quick_push (per_node_data ()); 1595 1596 for (int i = 0; i < m_sg.num_nodes (); i++) 1597 if (m_per_node[i].m_index == -1) 1598 strong_connect (i); 1599 1600 if (0) 1601 dump (); 1602 } 1603 1604 /* Dump this object to stderr. */ 1605 1606 DEBUG_FUNCTION void 1607 strongly_connected_components::dump () const 1608 { 1609 for (int i = 0; i < m_sg.num_nodes (); i++) 1610 { 1611 const per_node_data &v = m_per_node[i]; 1612 fprintf (stderr, "SN %i: index: %i lowlink: %i on_stack: %i\n", 1613 i, v.m_index, v.m_lowlink, v.m_on_stack); 1614 } 1615 } 1616 1617 /* Subroutine of strongly_connected_components's ctor, part of Tarjan's 1618 SCC algorithm. */ 1619 1620 void 1621 strongly_connected_components::strong_connect (unsigned index) 1622 { 1623 supernode *v_snode = m_sg.get_node_by_index (index); 1624 1625 /* Set the depth index for v to the smallest unused index. */ 1626 per_node_data *v = &m_per_node[index]; 1627 v->m_index = index; 1628 v->m_lowlink = index; 1629 m_stack.safe_push (index); 1630 v->m_on_stack = true; 1631 index++; 1632 1633 /* Consider successors of v. */ 1634 unsigned i; 1635 superedge *sedge; 1636 FOR_EACH_VEC_ELT (v_snode->m_succs, i, sedge) 1637 { 1638 supernode *w_snode = sedge->m_dest; 1639 per_node_data *w = &m_per_node[w_snode->m_index]; 1640 if (w->m_index == -1) 1641 { 1642 /* Successor w has not yet been visited; recurse on it. */ 1643 strong_connect (w_snode->m_index); 1644 v->m_lowlink = MIN (v->m_lowlink, w->m_lowlink); 1645 } 1646 else if (w->m_on_stack) 1647 { 1648 /* Successor w is in stack S and hence in the current SCC 1649 If w is not on stack, then (v, w) is a cross-edge in the DFS 1650 tree and must be ignored. */ 1651 v->m_lowlink = MIN (v->m_lowlink, w->m_index); 1652 } 1653 } 1654 1655 /* If v is a root node, pop the stack and generate an SCC. */ 1656 1657 if (v->m_lowlink == v->m_index) 1658 { 1659 per_node_data *w; 1660 do { 1661 int idx = m_stack.pop (); 1662 w = &m_per_node[idx]; 1663 w->m_on_stack = false; 1664 } while (w != v); 1665 } 1666 } 1667 1668 /* worklist's ctor. */ 1669 1670 worklist::worklist (const exploded_graph &eg, const analysis_plan &plan) 1671 : m_scc (eg.get_supergraph (), eg.get_logger ()), 1672 m_plan (plan), 1673 m_queue (key_t (*this, NULL)) 1674 { 1675 } 1676 1677 /* Return the number of nodes in the worklist. */ 1678 1679 unsigned 1680 worklist::length () const 1681 { 1682 return m_queue.nodes (); 1683 } 1684 1685 /* Return the next node in the worklist, removing it. */ 1686 1687 exploded_node * 1688 worklist::take_next () 1689 { 1690 return m_queue.extract_min (); 1691 } 1692 1693 /* Return the next node in the worklist without removing it. */ 1694 1695 exploded_node * 1696 worklist::peek_next () 1697 { 1698 return m_queue.min (); 1699 } 1700 1701 /* Add ENODE to the worklist. */ 1702 1703 void 1704 worklist::add_node (exploded_node *enode) 1705 { 1706 gcc_assert (enode->get_status () == exploded_node::STATUS_WORKLIST); 1707 m_queue.insert (key_t (*this, enode), enode); 1708 } 1709 1710 /* Comparator for implementing worklist::key_t comparison operators. 1711 Return negative if KA is before KB 1712 Return positive if KA is after KB 1713 Return 0 if they are equal. */ 1714 1715 int 1716 worklist::key_t::cmp (const worklist::key_t &ka, const worklist::key_t &kb) 1717 { 1718 const program_point &point_a = ka.m_enode->get_point (); 1719 const program_point &point_b = kb.m_enode->get_point (); 1720 const call_string &call_string_a = point_a.get_call_string (); 1721 const call_string &call_string_b = point_b.get_call_string (); 1722 1723 /* Order empty-callstring points with different functions based on the 1724 analysis_plan, so that we generate summaries before they are used. */ 1725 if (flag_analyzer_call_summaries 1726 && call_string_a.empty_p () 1727 && call_string_b.empty_p () 1728 && point_a.get_function () != NULL 1729 && point_b.get_function () != NULL 1730 && point_a.get_function () != point_b.get_function ()) 1731 { 1732 return ka.m_worklist.m_plan.cmp_function (point_a.get_function (), 1733 point_b.get_function ()); 1734 } 1735 1736 /* First, order by SCC. */ 1737 int scc_id_a = ka.get_scc_id (ka.m_enode); 1738 int scc_id_b = kb.get_scc_id (kb.m_enode); 1739 if (scc_id_a != scc_id_b) 1740 return scc_id_a - scc_id_b; 1741 1742 /* If in same SCC, order by supernode index (an arbitrary but stable 1743 ordering). */ 1744 const supernode *snode_a = ka.m_enode->get_supernode (); 1745 const supernode *snode_b = kb.m_enode->get_supernode (); 1746 if (snode_a == NULL) 1747 { 1748 if (snode_b != NULL) 1749 /* One is NULL. */ 1750 return -1; 1751 else 1752 /* Both are NULL. */ 1753 return 0; 1754 } 1755 if (snode_b == NULL) 1756 /* One is NULL. */ 1757 return 1; 1758 /* Neither are NULL. */ 1759 gcc_assert (snode_a && snode_b); 1760 if (snode_a->m_index != snode_b->m_index) 1761 return snode_a->m_index - snode_b->m_index; 1762 1763 gcc_assert (snode_a == snode_b); 1764 1765 /* Order within supernode via program point. */ 1766 int within_snode_cmp 1767 = function_point::cmp_within_supernode (point_a.get_function_point (), 1768 point_b.get_function_point ()); 1769 if (within_snode_cmp) 1770 return within_snode_cmp; 1771 1772 /* The points might vary by callstring; try sorting by callstring. */ 1773 int cs_cmp = call_string::cmp (call_string_a, call_string_b); 1774 if (cs_cmp) 1775 return cs_cmp; 1776 1777 /* Otherwise, we ought to have the same program_point. */ 1778 gcc_assert (point_a == point_b); 1779 1780 const program_state &state_a = ka.m_enode->get_state (); 1781 const program_state &state_b = kb.m_enode->get_state (); 1782 1783 /* Sort by sm-state, so that identical sm-states are grouped 1784 together in the worklist. 1785 For now, sort by the hash value (might not be deterministic). */ 1786 for (unsigned sm_idx = 0; sm_idx < state_a.m_checker_states.length (); 1787 ++sm_idx) 1788 { 1789 sm_state_map *smap_a = state_a.m_checker_states[sm_idx]; 1790 sm_state_map *smap_b = state_b.m_checker_states[sm_idx]; 1791 1792 hashval_t hash_a = smap_a->hash (); 1793 hashval_t hash_b = smap_b->hash (); 1794 if (hash_a < hash_b) 1795 return -1; 1796 else if (hash_a > hash_b) 1797 return 1; 1798 } 1799 1800 /* Otherwise, we have two enodes at the same program point but with 1801 different states. We don't have a good total ordering on states, 1802 so order them by enode index, so that we have at least have a 1803 stable sort. */ 1804 return ka.m_enode->m_index - kb.m_enode->m_index; 1805 } 1806 1807 /* exploded_graph's ctor. */ 1808 1809 exploded_graph::exploded_graph (const supergraph &sg, logger *logger, 1810 const extrinsic_state &ext_state, 1811 const state_purge_map *purge_map, 1812 const analysis_plan &plan, 1813 int verbosity) 1814 : m_sg (sg), m_logger (logger), 1815 m_worklist (*this, plan), 1816 m_ext_state (ext_state), 1817 m_purge_map (purge_map), 1818 m_plan (plan), 1819 m_diagnostic_manager (logger, verbosity), 1820 m_global_stats (m_sg.num_nodes ()), 1821 m_functionless_stats (m_sg.num_nodes ()), 1822 m_PK_AFTER_SUPERNODE_per_snode (m_sg.num_nodes ()) 1823 { 1824 m_origin = get_or_create_node (program_point (function_point (NULL, NULL, 1825 0, PK_ORIGIN), 1826 call_string ()), 1827 program_state (ext_state), NULL); 1828 for (int i = 0; i < m_sg.num_nodes (); i++) 1829 m_PK_AFTER_SUPERNODE_per_snode.quick_push (i); 1830 } 1831 1832 /* exploded_graph's dtor. */ 1833 1834 exploded_graph::~exploded_graph () 1835 { 1836 for (function_stat_map_t::iterator iter = m_per_function_stats.begin (); 1837 iter != m_per_function_stats.end (); 1838 ++iter) 1839 delete (*iter).second; 1840 1841 for (point_map_t::iterator iter = m_per_point_data.begin (); 1842 iter != m_per_point_data.end (); 1843 ++iter) 1844 delete (*iter).second; 1845 } 1846 1847 /* Ensure that there is an exploded_node representing an external call to 1848 FUN, adding it to the worklist if creating it. 1849 1850 Add an edge from the origin exploded_node to the function entrypoint 1851 exploded_node. 1852 1853 Return the exploded_node for the entrypoint to the function. */ 1854 1855 exploded_node * 1856 exploded_graph::add_function_entry (function *fun) 1857 { 1858 program_point point = program_point::from_function_entry (m_sg, fun); 1859 program_state state (m_ext_state); 1860 impl_region_model_context ctxt (&state, NULL, m_ext_state, get_logger ()); 1861 state.m_region_model->push_frame (fun, NULL, &ctxt); 1862 1863 if (!state.m_valid) 1864 return NULL; 1865 1866 exploded_node *enode = get_or_create_node (point, state, NULL); 1867 /* We should never fail to add such a node. */ 1868 gcc_assert (enode); 1869 state_change change; 1870 add_edge (m_origin, enode, NULL, change); 1871 return enode; 1872 } 1873 1874 /* Get or create an exploded_node for (POINT, STATE). 1875 If a new node is created, it is added to the worklist. 1876 If CHANGE is non-NULL, use it to suppress some purging of state, 1877 to make generation of state_change_event instances easier. */ 1878 1879 exploded_node * 1880 exploded_graph::get_or_create_node (const program_point &point, 1881 const program_state &state, 1882 state_change *change) 1883 { 1884 logger * const logger = get_logger (); 1885 LOG_FUNC (logger); 1886 if (logger) 1887 { 1888 format f (false); 1889 pretty_printer *pp = logger->get_printer (); 1890 logger->start_log_line (); 1891 pp_string (pp, "point: "); 1892 point.print (pp, f); 1893 logger->end_log_line (); 1894 logger->start_log_line (); 1895 pp_string (pp, "state: "); 1896 state.dump_to_pp (m_ext_state, true, pp); 1897 logger->end_log_line (); 1898 } 1899 1900 /* Stop exploring paths for which we don't know how to effectively 1901 model the state. */ 1902 if (!state.m_valid) 1903 { 1904 if (logger) 1905 logger->log ("invalid state; not creating node"); 1906 return NULL; 1907 } 1908 1909 auto_cfun sentinel (point.get_function ()); 1910 1911 state.validate (get_ext_state ()); 1912 1913 //state.dump (get_ext_state ()); 1914 1915 /* Prune state to try to improve the chances of a cache hit, 1916 avoiding generating redundant nodes. */ 1917 program_state pruned_state = state.prune_for_point (*this, point, change); 1918 1919 pruned_state.validate (get_ext_state ()); 1920 1921 //pruned_state.dump (get_ext_state ()); 1922 1923 if (logger) 1924 { 1925 pretty_printer *pp = logger->get_printer (); 1926 logger->start_log_line (); 1927 pp_string (pp, "pruned_state: "); 1928 pruned_state.dump_to_pp (m_ext_state, true, pp); 1929 logger->end_log_line (); 1930 pruned_state.m_region_model->dump_to_pp (logger->get_printer (), true); 1931 } 1932 1933 stats *per_fn_stats = get_or_create_function_stats (point.get_function ()); 1934 1935 stats *per_cs_stats 1936 = &get_or_create_per_call_string_data (point.get_call_string ())->m_stats; 1937 1938 point_and_state ps (point, pruned_state); 1939 ps.validate (m_ext_state); 1940 if (exploded_node **slot = m_point_and_state_to_node.get (&ps)) 1941 { 1942 /* An exploded_node for PS already exists. */ 1943 if (logger) 1944 logger->log ("reused EN: %i", (*slot)->m_index); 1945 m_global_stats.m_node_reuse_count++; 1946 per_fn_stats->m_node_reuse_count++; 1947 per_cs_stats->m_node_reuse_count++; 1948 return *slot; 1949 } 1950 1951 per_program_point_data *per_point_data 1952 = get_or_create_per_program_point_data (point); 1953 1954 /* Consider merging state with another enode at this program_point. */ 1955 if (flag_analyzer_state_merge) 1956 { 1957 exploded_node *existing_enode; 1958 unsigned i; 1959 FOR_EACH_VEC_ELT (per_point_data->m_enodes, i, existing_enode) 1960 { 1961 if (logger) 1962 logger->log ("considering merging with existing EN: %i for point", 1963 existing_enode->m_index); 1964 gcc_assert (existing_enode->get_point () == point); 1965 const program_state &existing_state = existing_enode->get_state (); 1966 1967 /* This merges successfully within the loop. */ 1968 1969 program_state merged_state (m_ext_state); 1970 if (pruned_state.can_merge_with_p (existing_state, m_ext_state, 1971 &merged_state)) 1972 { 1973 if (logger) 1974 logger->log ("merging new state with that of EN: %i", 1975 existing_enode->m_index); 1976 1977 /* Try again for a cache hit. 1978 Whether we get one or not, merged_state's value_ids have no 1979 relationship to those of the input state, and thus to those 1980 of CHANGE, so we must purge any svalue_ids from *CHANGE. */ 1981 ps.set_state (merged_state); 1982 if (change) 1983 change->on_svalue_purge (svalue_id::from_int (0)); 1984 1985 if (exploded_node **slot = m_point_and_state_to_node.get (&ps)) 1986 { 1987 /* An exploded_node for PS already exists. */ 1988 if (logger) 1989 logger->log ("reused EN: %i", (*slot)->m_index); 1990 m_global_stats.m_node_reuse_after_merge_count++; 1991 per_fn_stats->m_node_reuse_after_merge_count++; 1992 per_cs_stats->m_node_reuse_after_merge_count++; 1993 return *slot; 1994 } 1995 } 1996 else 1997 if (logger) 1998 logger->log ("not merging new state with that of EN: %i", 1999 existing_enode->m_index); 2000 } 2001 } 2002 2003 /* Impose a limit on the number of enodes per program point, and 2004 simply stop if we exceed it. */ 2005 if ((int)per_point_data->m_enodes.length () 2006 > param_analyzer_max_enodes_per_program_point) 2007 { 2008 if (logger) 2009 logger->log ("not creating enode; too many at program point"); 2010 warning_at (point.get_location (), OPT_Wanalyzer_too_complex, 2011 "terminating analysis for this program point"); 2012 per_point_data->m_excess_enodes++; 2013 return NULL; 2014 } 2015 2016 ps.validate (m_ext_state); 2017 2018 /* An exploded_node for "ps" doesn't already exist; create one. */ 2019 exploded_node *node = new exploded_node (ps, m_nodes.length ()); 2020 add_node (node); 2021 m_point_and_state_to_node.put (node->get_ps_key (), node); 2022 2023 /* Update per-program_point data. */ 2024 per_point_data->m_enodes.safe_push (node); 2025 2026 const enum point_kind node_pk = node->get_point ().get_kind (); 2027 m_global_stats.m_num_nodes[node_pk]++; 2028 per_fn_stats->m_num_nodes[node_pk]++; 2029 per_cs_stats->m_num_nodes[node_pk]++; 2030 2031 if (node_pk == PK_AFTER_SUPERNODE) 2032 m_PK_AFTER_SUPERNODE_per_snode[point.get_supernode ()->m_index]++; 2033 2034 if (logger) 2035 { 2036 format f (false); 2037 pretty_printer *pp = logger->get_printer (); 2038 logger->log ("created EN: %i", node->m_index); 2039 logger->start_log_line (); 2040 pp_string (pp, "point: "); 2041 point.print (pp, f); 2042 logger->end_log_line (); 2043 logger->start_log_line (); 2044 pp_string (pp, "pruned_state: "); 2045 pruned_state.dump_to_pp (m_ext_state, true, pp); 2046 logger->end_log_line (); 2047 } 2048 2049 /* Add the new node to the worlist. */ 2050 m_worklist.add_node (node); 2051 return node; 2052 } 2053 2054 /* Add an exploded_edge from SRC to DEST, recording its association 2055 with SEDGE (which may be NULL), and, if non-NULL, taking ownership 2056 of REWIND_INFO. 2057 Return the newly-created eedge. */ 2058 2059 exploded_edge * 2060 exploded_graph::add_edge (exploded_node *src, exploded_node *dest, 2061 const superedge *sedge, 2062 const state_change &change, 2063 exploded_edge::custom_info_t *custom_info) 2064 { 2065 exploded_edge *e = new exploded_edge (src, dest, m_ext_state, 2066 sedge, change, custom_info); 2067 digraph<eg_traits>::add_edge (e); 2068 return e; 2069 } 2070 2071 /* Ensure that this graph has per-program_point-data for POINT; 2072 borrow a pointer to it. */ 2073 2074 per_program_point_data * 2075 exploded_graph:: 2076 get_or_create_per_program_point_data (const program_point &point) 2077 { 2078 if (per_program_point_data **slot = m_per_point_data.get (&point)) 2079 return *slot; 2080 2081 per_program_point_data *per_point_data = new per_program_point_data (point); 2082 m_per_point_data.put (&per_point_data->m_key, per_point_data); 2083 return per_point_data; 2084 } 2085 2086 /* Ensure that this graph has per-call_string-data for CS; 2087 borrow a pointer to it. */ 2088 2089 per_call_string_data * 2090 exploded_graph::get_or_create_per_call_string_data (const call_string &cs) 2091 { 2092 if (per_call_string_data **slot = m_per_call_string_data.get (&cs)) 2093 return *slot; 2094 2095 per_call_string_data *data = new per_call_string_data (cs, m_sg.num_nodes ()); 2096 m_per_call_string_data.put (&data->m_key, 2097 data); 2098 return data; 2099 } 2100 2101 /* Ensure that this graph has per-function-data for FUN; 2102 borrow a pointer to it. */ 2103 2104 per_function_data * 2105 exploded_graph::get_or_create_per_function_data (function *fun) 2106 { 2107 if (per_function_data **slot = m_per_function_data.get (fun)) 2108 return *slot; 2109 2110 per_function_data *data = new per_function_data (); 2111 m_per_function_data.put (fun, data); 2112 return data; 2113 } 2114 2115 /* Get this graph's per-function-data for FUN if there is any, 2116 otherwise NULL. */ 2117 2118 per_function_data * 2119 exploded_graph::get_per_function_data (function *fun) const 2120 { 2121 if (per_function_data **slot 2122 = const_cast <per_function_data_t &> (m_per_function_data).get (fun)) 2123 return *slot; 2124 2125 return NULL; 2126 } 2127 2128 /* Return true if NODE and FUN should be traversed directly, rather than 2129 called via other functions. */ 2130 2131 static bool 2132 toplevel_function_p (cgraph_node *node, function *fun, logger *logger) 2133 { 2134 /* TODO: better logic here 2135 e.g. only if more than one caller, and significantly complicated. 2136 Perhaps some whole-callgraph analysis to decide if it's worth summarizing 2137 an edge, and if so, we need summaries. */ 2138 if (flag_analyzer_call_summaries) 2139 { 2140 int num_call_sites = 0; 2141 for (cgraph_edge *edge = node->callers; edge; edge = edge->next_caller) 2142 ++num_call_sites; 2143 2144 /* For now, if there's more than one in-edge, and we want call 2145 summaries, do it at the top level so that there's a chance 2146 we'll have a summary when we need one. */ 2147 if (num_call_sites > 1) 2148 { 2149 if (logger) 2150 logger->log ("traversing %qE (%i call sites)", 2151 fun->decl, num_call_sites); 2152 return true; 2153 } 2154 } 2155 2156 if (!TREE_PUBLIC (fun->decl)) 2157 { 2158 if (logger) 2159 logger->log ("not traversing %qE (static)", fun->decl); 2160 return false; 2161 } 2162 2163 if (logger) 2164 logger->log ("traversing %qE (all checks passed)", fun->decl); 2165 2166 return true; 2167 } 2168 2169 /* Add initial nodes to EG, with entrypoints for externally-callable 2170 functions. */ 2171 2172 void 2173 exploded_graph::build_initial_worklist () 2174 { 2175 logger * const logger = get_logger (); 2176 LOG_SCOPE (logger); 2177 2178 cgraph_node *node; 2179 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node) 2180 { 2181 function *fun = node->get_fun (); 2182 if (!toplevel_function_p (node, fun, logger)) 2183 continue; 2184 exploded_node *enode = add_function_entry (fun); 2185 if (logger) 2186 { 2187 if (enode) 2188 logger->log ("created EN %i for %qE entrypoint", 2189 enode->m_index, fun->decl); 2190 else 2191 logger->log ("did not create enode for %qE entrypoint", fun->decl); 2192 } 2193 } 2194 } 2195 2196 /* The main loop of the analysis. 2197 Take freshly-created exploded_nodes from the worklist, calling 2198 process_node on them to explore the <point, state> graph. 2199 Add edges to their successors, potentially creating new successors 2200 (which are also added to the worklist). */ 2201 2202 void 2203 exploded_graph::process_worklist () 2204 { 2205 logger * const logger = get_logger (); 2206 LOG_SCOPE (logger); 2207 auto_timevar tv (TV_ANALYZER_WORKLIST); 2208 2209 while (m_worklist.length () > 0) 2210 { 2211 exploded_node *node = m_worklist.take_next (); 2212 gcc_assert (node->get_status () == exploded_node::STATUS_WORKLIST); 2213 gcc_assert (node->m_succs.length () == 0 2214 || node == m_origin); 2215 2216 if (logger) 2217 logger->log ("next to process: EN: %i", node->m_index); 2218 2219 /* Avoid exponential explosions of nodes by attempting to merge 2220 nodes that are at the same program point and which have 2221 sufficiently similar state. */ 2222 if (flag_analyzer_state_merge && node != m_origin) 2223 if (exploded_node *node_2 = m_worklist.peek_next ()) 2224 { 2225 gcc_assert (node_2->get_status () 2226 == exploded_node::STATUS_WORKLIST); 2227 gcc_assert (node->m_succs.length () == 0); 2228 gcc_assert (node_2->m_succs.length () == 0); 2229 2230 gcc_assert (node != node_2); 2231 2232 if (logger) 2233 logger->log ("peek worklist: EN: %i", node_2->m_index); 2234 2235 if (node->get_point () == node_2->get_point ()) 2236 { 2237 if (logger) 2238 { 2239 format f (false); 2240 pretty_printer *pp = logger->get_printer (); 2241 logger->start_log_line (); 2242 logger->log_partial 2243 ("got potential merge EN: %i and EN: %i at ", 2244 node->m_index, node_2->m_index); 2245 node->get_point ().print (pp, f); 2246 logger->end_log_line (); 2247 } 2248 2249 const program_state &state = node->get_state (); 2250 const program_state &state_2 = node_2->get_state (); 2251 2252 /* They shouldn't be equal, or we wouldn't have two 2253 separate nodes. */ 2254 gcc_assert (state != state_2); 2255 2256 program_state merged_state (m_ext_state); 2257 state_change change; 2258 if (state.can_merge_with_p (state_2, m_ext_state, 2259 &merged_state)) 2260 { 2261 if (logger) 2262 logger->log ("merging EN: %i and EN: %i", 2263 node->m_index, node_2->m_index); 2264 2265 if (merged_state == state) 2266 { 2267 /* Then merge node_2 into node by adding an edge. */ 2268 add_edge (node_2, node, NULL, change); 2269 2270 /* Remove node_2 from the worklist. */ 2271 m_worklist.take_next (); 2272 node_2->set_status (exploded_node::STATUS_MERGER); 2273 2274 /* Continue processing "node" below. */ 2275 } 2276 else if (merged_state == state_2) 2277 { 2278 /* Then merge node into node_2, and leave node_2 2279 in the worklist, to be processed on the next 2280 iteration. */ 2281 add_edge (node, node_2, NULL, change); 2282 node->set_status (exploded_node::STATUS_MERGER); 2283 continue; 2284 } 2285 else 2286 { 2287 /* We have a merged state that differs from 2288 both state and state_2. */ 2289 2290 /* Remove node_2 from the worklist. */ 2291 m_worklist.take_next (); 2292 2293 /* Create (or get) an exploded node for the merged 2294 states, adding to the worklist. */ 2295 exploded_node *merged_enode 2296 = get_or_create_node (node->get_point (), 2297 merged_state, &change); 2298 if (merged_enode == NULL) 2299 continue; 2300 2301 if (logger) 2302 logger->log ("merged EN: %i and EN: %i into EN: %i", 2303 node->m_index, node_2->m_index, 2304 merged_enode->m_index); 2305 2306 /* "node" and "node_2" have both now been removed 2307 from the worklist; we should not process them. 2308 2309 "merged_enode" may be a new node; if so it will be 2310 processed in a subsequent iteration. 2311 Alternatively, "merged_enode" could be an existing 2312 node; one way the latter can 2313 happen is if we end up merging a succession of 2314 similar nodes into one. */ 2315 2316 /* If merged_node is one of the two we were merging, 2317 add it back to the worklist to ensure it gets 2318 processed. 2319 2320 Add edges from the merged nodes to it (but not a 2321 self-edge). */ 2322 if (merged_enode == node) 2323 m_worklist.add_node (merged_enode); 2324 else 2325 { 2326 add_edge (node, merged_enode, NULL, change); 2327 node->set_status (exploded_node::STATUS_MERGER); 2328 } 2329 2330 if (merged_enode == node_2) 2331 m_worklist.add_node (merged_enode); 2332 else 2333 { 2334 add_edge (node_2, merged_enode, NULL, change); 2335 node_2->set_status (exploded_node::STATUS_MERGER); 2336 } 2337 2338 continue; 2339 } 2340 } 2341 2342 /* TODO: should we attempt more than two nodes, 2343 or just do pairs of nodes? (and hope that we get 2344 a cascade of mergers). */ 2345 } 2346 } 2347 2348 process_node (node); 2349 2350 /* Impose a hard limit on the number of exploded nodes, to ensure 2351 that the analysis terminates in the face of pathological state 2352 explosion (or bugs). 2353 2354 Specifically, the limit is on the number of PK_AFTER_SUPERNODE 2355 exploded nodes, looking at supernode exit events. 2356 2357 We use exit rather than entry since there can be multiple 2358 entry ENs, one per phi; the number of PK_AFTER_SUPERNODE ought 2359 to be equivalent to the number of supernodes multiplied by the 2360 number of states. */ 2361 const int limit = m_sg.num_nodes () * param_analyzer_bb_explosion_factor; 2362 if (m_global_stats.m_num_nodes[PK_AFTER_SUPERNODE] > limit) 2363 { 2364 if (logger) 2365 logger->log ("bailing out; too many nodes"); 2366 warning_at (node->get_point ().get_location (), 2367 OPT_Wanalyzer_too_complex, 2368 "analysis bailed out early" 2369 " (%i 'after-snode' enodes; %i enodes)", 2370 m_global_stats.m_num_nodes[PK_AFTER_SUPERNODE], 2371 m_nodes.length ()); 2372 return; 2373 } 2374 } 2375 } 2376 2377 /* Return true if STMT must appear at the start of its exploded node, and 2378 thus we can't consolidate its effects within a run of other statements, 2379 where PREV_STMT was the previous statement. */ 2380 2381 static bool 2382 stmt_requires_new_enode_p (const gimple *stmt, 2383 const gimple *prev_stmt) 2384 { 2385 /* Stop consolidating at calls to 2386 "__analyzer_dump_exploded_nodes", so they always appear at the 2387 start of an exploded_node. */ 2388 if (const gcall *call = dyn_cast <const gcall *> (stmt)) 2389 if (is_special_named_call_p (call, "__analyzer_dump_exploded_nodes", 2390 1)) 2391 return true; 2392 2393 /* If we had a PREV_STMT with an unknown location, and this stmt 2394 has a known location, then if a state change happens here, it 2395 could be consolidated into PREV_STMT, giving us an event with 2396 no location. Ensure that STMT gets its own exploded_node to 2397 avoid this. */ 2398 if (get_pure_location (prev_stmt->location) == UNKNOWN_LOCATION 2399 && get_pure_location (stmt->location) != UNKNOWN_LOCATION) 2400 return true; 2401 2402 return false; 2403 } 2404 2405 /* The core of exploded_graph::process_worklist (the main analysis loop), 2406 handling one node in the worklist. 2407 2408 Get successor <point, state> pairs for NODE, calling get_or_create on 2409 them, and adding an exploded_edge to each successors. 2410 2411 Freshly-created nodes will be added to the worklist. */ 2412 2413 void 2414 exploded_graph::process_node (exploded_node *node) 2415 { 2416 logger * const logger = get_logger (); 2417 LOG_FUNC_1 (logger, "EN: %i", node->m_index); 2418 2419 node->set_status (exploded_node::STATUS_PROCESSED); 2420 2421 const program_point &point = node->get_point (); 2422 2423 /* Update cfun and input_location in case of an ICE: make it easier to 2424 track down which source construct we're failing to handle. */ 2425 auto_cfun sentinel (node->get_function ()); 2426 const gimple *stmt = point.get_stmt (); 2427 if (stmt) 2428 input_location = stmt->location; 2429 2430 const program_state &state = node->get_state (); 2431 if (logger) 2432 { 2433 pretty_printer *pp = logger->get_printer (); 2434 logger->start_log_line (); 2435 pp_string (pp, "point: "); 2436 point.print (pp, format (false)); 2437 pp_string (pp, ", state: "); 2438 state.dump_to_pp (m_ext_state, true, pp); 2439 logger->end_log_line (); 2440 } 2441 2442 switch (point.get_kind ()) 2443 { 2444 default: 2445 gcc_unreachable (); 2446 case PK_ORIGIN: 2447 /* This node exists to simplify finding the shortest path 2448 to an exploded_node. */ 2449 break; 2450 2451 case PK_BEFORE_SUPERNODE: 2452 { 2453 program_state next_state (state); 2454 state_change change; 2455 2456 if (point.get_from_edge ()) 2457 { 2458 impl_region_model_context ctxt (*this, node, 2459 &state, &next_state, &change, 2460 NULL); 2461 const cfg_superedge *last_cfg_superedge 2462 = point.get_from_edge ()->dyn_cast_cfg_superedge (); 2463 if (last_cfg_superedge) 2464 next_state.m_region_model->update_for_phis 2465 (node->get_supernode (), 2466 last_cfg_superedge, 2467 &ctxt); 2468 } 2469 2470 if (point.get_supernode ()->m_stmts.length () > 0) 2471 { 2472 program_point next_point 2473 = program_point::before_stmt (point.get_supernode (), 0, 2474 point.get_call_string ()); 2475 exploded_node *next 2476 = get_or_create_node (next_point, next_state, &change); 2477 if (next) 2478 add_edge (node, next, NULL, change); 2479 } 2480 else 2481 { 2482 program_point next_point 2483 = program_point::after_supernode (point.get_supernode (), 2484 point.get_call_string ()); 2485 exploded_node *next = get_or_create_node (next_point, next_state, 2486 &change); 2487 if (next) 2488 add_edge (node, next, NULL, change); 2489 } 2490 } 2491 break; 2492 case PK_BEFORE_STMT: 2493 { 2494 /* Determine the effect of a run of one or more statements 2495 within one supernode, generating an edge to the program_point 2496 after the last statement that's processed. 2497 2498 Stop iterating statements and thus consolidating into one enode 2499 when: 2500 - reaching the end of the statements in the supernode 2501 - if an sm-state-change occurs (so that it gets its own 2502 exploded_node) 2503 - if "-fanalyzer-fine-grained" is active 2504 - encountering certain statements must appear at the start of 2505 their enode (for which stmt_requires_new_enode_p returns true) 2506 2507 Update next_state in-place, to get the result of the one 2508 or more stmts that are processed. */ 2509 program_state next_state (state); 2510 state_change change; 2511 const supernode *snode = point.get_supernode (); 2512 unsigned stmt_idx; 2513 const gimple *prev_stmt = NULL; 2514 for (stmt_idx = point.get_stmt_idx (); 2515 stmt_idx < snode->m_stmts.length (); 2516 stmt_idx++) 2517 { 2518 const gimple *stmt = snode->m_stmts[stmt_idx]; 2519 2520 if (stmt_idx > point.get_stmt_idx ()) 2521 if (stmt_requires_new_enode_p (stmt, prev_stmt)) 2522 { 2523 stmt_idx--; 2524 break; 2525 } 2526 prev_stmt = stmt; 2527 2528 /* Process the stmt. */ 2529 exploded_node::on_stmt_flags flags 2530 = node->on_stmt (*this, snode, stmt, &next_state, &change); 2531 2532 /* If flags.m_terminate_path, stop analyzing; any nodes/edges 2533 will have been added by on_stmt (e.g. for handling longjmp). */ 2534 if (flags.m_terminate_path) 2535 return; 2536 2537 if (flags.m_sm_changes || flag_analyzer_fine_grained) 2538 break; 2539 } 2540 unsigned next_idx = stmt_idx + 1; 2541 program_point next_point 2542 = (next_idx < point.get_supernode ()->m_stmts.length () 2543 ? program_point::before_stmt (point.get_supernode (), next_idx, 2544 point.get_call_string ()) 2545 : program_point::after_supernode (point.get_supernode (), 2546 point.get_call_string ())); 2547 exploded_node *next = get_or_create_node (next_point, 2548 next_state, &change); 2549 if (next) 2550 add_edge (node, next, NULL, change); 2551 } 2552 break; 2553 case PK_AFTER_SUPERNODE: 2554 { 2555 /* If this is an EXIT BB, detect leaks, and potentially 2556 create a function summary. */ 2557 if (point.get_supernode ()->return_p ()) 2558 { 2559 node->detect_leaks (*this); 2560 if (flag_analyzer_call_summaries 2561 && point.get_call_string ().empty_p ()) 2562 { 2563 /* TODO: create function summary 2564 There can be more than one; each corresponds to a different 2565 final enode in the function. */ 2566 if (logger) 2567 { 2568 pretty_printer *pp = logger->get_printer (); 2569 logger->start_log_line (); 2570 logger->log_partial 2571 ("would create function summary for %qE; state: ", 2572 point.get_fndecl ()); 2573 state.dump_to_pp (m_ext_state, true, pp); 2574 logger->end_log_line (); 2575 } 2576 per_function_data *per_fn_data 2577 = get_or_create_per_function_data (point.get_function ()); 2578 per_fn_data->add_call_summary (node); 2579 } 2580 } 2581 /* Traverse into successors of the supernode. */ 2582 int i; 2583 superedge *succ; 2584 FOR_EACH_VEC_ELT (point.get_supernode ()->m_succs, i, succ) 2585 { 2586 if (logger) 2587 logger->log ("considering SN: %i -> SN: %i", 2588 succ->m_src->m_index, succ->m_dest->m_index); 2589 2590 state_change change; 2591 2592 program_point next_point 2593 = program_point::before_supernode (succ->m_dest, succ, 2594 point.get_call_string ()); 2595 program_state next_state (state); 2596 2597 if (!node->on_edge (*this, succ, &next_point, &next_state, &change)) 2598 { 2599 if (logger) 2600 logger->log ("skipping impossible edge to SN: %i", 2601 succ->m_dest->m_index); 2602 continue; 2603 } 2604 2605 exploded_node *next = get_or_create_node (next_point, next_state, 2606 &change); 2607 if (next) 2608 add_edge (node, next, succ, change); 2609 } 2610 } 2611 break; 2612 } 2613 } 2614 2615 /* Ensure that this graph has a stats instance for FN, return it. 2616 FN can be NULL, in which case a stats instances is returned covering 2617 "functionless" parts of the graph (the origin node). */ 2618 2619 stats * 2620 exploded_graph::get_or_create_function_stats (function *fn) 2621 { 2622 if (!fn) 2623 return &m_functionless_stats; 2624 2625 if (stats **slot = m_per_function_stats.get (fn)) 2626 return *slot; 2627 else 2628 { 2629 int num_supernodes = fn ? n_basic_blocks_for_fn (fn) : 0; 2630 /* not quite the num supernodes, but nearly. */ 2631 stats *new_stats = new stats (num_supernodes); 2632 m_per_function_stats.put (fn, new_stats); 2633 return new_stats; 2634 } 2635 } 2636 2637 /* Print bar charts to PP showing: 2638 - the number of enodes per function, and 2639 - for each function: 2640 - the number of enodes per supernode/BB 2641 - the number of excess enodes per supernode/BB beyond the 2642 per-program-point limit, if there were any. */ 2643 2644 void 2645 exploded_graph::print_bar_charts (pretty_printer *pp) const 2646 { 2647 cgraph_node *cgnode; 2648 2649 pp_string (pp, "enodes per function:"); 2650 pp_newline (pp); 2651 bar_chart enodes_per_function; 2652 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (cgnode) 2653 { 2654 function *fn = cgnode->get_fun (); 2655 const stats * const *s_ptr 2656 = const_cast <function_stat_map_t &> (m_per_function_stats).get (fn); 2657 enodes_per_function.add_item (function_name (fn), 2658 s_ptr ? (*s_ptr)->get_total_enodes () : 0); 2659 } 2660 enodes_per_function.print (pp); 2661 2662 /* Accumulate number of enodes per supernode. */ 2663 auto_vec<unsigned> enodes_per_supernode (m_sg.num_nodes ()); 2664 for (int i = 0; i < m_sg.num_nodes (); i++) 2665 enodes_per_supernode.quick_push (0); 2666 int i; 2667 exploded_node *enode; 2668 FOR_EACH_VEC_ELT (m_nodes, i, enode) 2669 { 2670 const supernode *iter_snode = enode->get_supernode (); 2671 if (!iter_snode) 2672 continue; 2673 enodes_per_supernode[iter_snode->m_index]++; 2674 } 2675 2676 /* Accumulate excess enodes per supernode. */ 2677 auto_vec<unsigned> excess_enodes_per_supernode (m_sg.num_nodes ()); 2678 for (int i = 0; i < m_sg.num_nodes (); i++) 2679 excess_enodes_per_supernode.quick_push (0); 2680 for (point_map_t::iterator iter = m_per_point_data.begin (); 2681 iter != m_per_point_data.end (); ++iter) 2682 { 2683 const program_point *point = (*iter).first; 2684 const supernode *iter_snode = point->get_supernode (); 2685 if (!iter_snode) 2686 continue; 2687 const per_program_point_data *point_data = (*iter).second; 2688 excess_enodes_per_supernode[iter_snode->m_index] 2689 += point_data->m_excess_enodes; 2690 } 2691 2692 /* Show per-function bar_charts of enodes per supernode/BB. */ 2693 pp_string (pp, "per-function enodes per supernode/BB:"); 2694 pp_newline (pp); 2695 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (cgnode) 2696 { 2697 function *fn = cgnode->get_fun (); 2698 pp_printf (pp, "function: %qs", function_name (fn)); 2699 pp_newline (pp); 2700 2701 bar_chart enodes_per_snode; 2702 bar_chart excess_enodes_per_snode; 2703 bool have_excess_enodes = false; 2704 for (int i = 0; i < m_sg.num_nodes (); i++) 2705 { 2706 const supernode *iter_snode = m_sg.get_node_by_index (i); 2707 if (iter_snode->get_function () != fn) 2708 continue; 2709 pretty_printer tmp_pp; 2710 pp_printf (&tmp_pp, "sn %i (bb %i)", 2711 iter_snode->m_index, iter_snode->m_bb->index); 2712 enodes_per_snode.add_item (pp_formatted_text (&tmp_pp), 2713 enodes_per_supernode[iter_snode->m_index]); 2714 const int num_excess 2715 = excess_enodes_per_supernode[iter_snode->m_index]; 2716 excess_enodes_per_snode.add_item (pp_formatted_text (&tmp_pp), 2717 num_excess); 2718 if (num_excess) 2719 have_excess_enodes = true; 2720 } 2721 enodes_per_snode.print (pp); 2722 if (have_excess_enodes) 2723 { 2724 pp_printf (pp, "EXCESS ENODES:"); 2725 pp_newline (pp); 2726 excess_enodes_per_snode.print (pp); 2727 } 2728 } 2729 } 2730 2731 /* Write all stats information to this graph's logger, if any. */ 2732 2733 void 2734 exploded_graph::log_stats () const 2735 { 2736 logger * const logger = get_logger (); 2737 if (!logger) 2738 return; 2739 2740 LOG_SCOPE (logger); 2741 2742 logger->log ("m_sg.num_nodes (): %i", m_sg.num_nodes ()); 2743 logger->log ("m_nodes.length (): %i", m_nodes.length ()); 2744 logger->log ("m_edges.length (): %i", m_edges.length ()); 2745 logger->log ("remaining enodes in worklist: %i", m_worklist.length ()); 2746 2747 logger->log ("global stats:"); 2748 m_global_stats.log (logger); 2749 2750 for (function_stat_map_t::iterator iter = m_per_function_stats.begin (); 2751 iter != m_per_function_stats.end (); 2752 ++iter) 2753 { 2754 function *fn = (*iter).first; 2755 log_scope s (logger, function_name (fn)); 2756 (*iter).second->log (logger); 2757 } 2758 2759 print_bar_charts (logger->get_printer ()); 2760 } 2761 2762 /* Dump all stats information to OUT. */ 2763 2764 void 2765 exploded_graph::dump_stats (FILE *out) const 2766 { 2767 fprintf (out, "m_sg.num_nodes (): %i\n", m_sg.num_nodes ()); 2768 fprintf (out, "m_nodes.length (): %i\n", m_nodes.length ()); 2769 fprintf (out, "m_edges.length (): %i\n", m_edges.length ()); 2770 fprintf (out, "remaining enodes in worklist: %i", m_worklist.length ()); 2771 2772 fprintf (out, "global stats:\n"); 2773 m_global_stats.dump (out); 2774 2775 for (function_stat_map_t::iterator iter = m_per_function_stats.begin (); 2776 iter != m_per_function_stats.end (); 2777 ++iter) 2778 { 2779 function *fn = (*iter).first; 2780 fprintf (out, "function: %s\n", function_name (fn)); 2781 (*iter).second->dump (out); 2782 } 2783 2784 fprintf (out, "PK_AFTER_SUPERNODE per supernode:\n"); 2785 for (unsigned i = 0; i < m_PK_AFTER_SUPERNODE_per_snode.length (); i++) 2786 fprintf (out, " SN %i: %3i\n", i, m_PK_AFTER_SUPERNODE_per_snode[i]); 2787 } 2788 2789 void 2790 exploded_graph::dump_states_for_supernode (FILE *out, 2791 const supernode *snode) const 2792 { 2793 fprintf (out, "PK_AFTER_SUPERNODE nodes for SN: %i\n", snode->m_index); 2794 int i; 2795 exploded_node *enode; 2796 int state_idx = 0; 2797 FOR_EACH_VEC_ELT (m_nodes, i, enode) 2798 { 2799 const supernode *iter_snode = enode->get_supernode (); 2800 if (enode->get_point ().get_kind () == PK_AFTER_SUPERNODE 2801 && iter_snode == snode) 2802 { 2803 pretty_printer pp; 2804 pp_format_decoder (&pp) = default_tree_printer; 2805 enode->get_state ().dump_to_pp (m_ext_state, true, &pp); 2806 fprintf (out, "state %i: EN: %i\n %s\n", 2807 state_idx++, enode->m_index, 2808 pp_formatted_text (&pp)); 2809 } 2810 } 2811 fprintf (out, "#exploded_node for PK_AFTER_SUPERNODE for SN: %i = %i\n", 2812 snode->m_index, state_idx); 2813 } 2814 2815 /* Look for the last use of SEARCH_STMT within this path. 2816 If found write the edge's index to *OUT_IDX and return true, otherwise 2817 return false. */ 2818 2819 bool 2820 exploded_path::find_stmt_backwards (const gimple *search_stmt, 2821 int *out_idx) const 2822 { 2823 int i; 2824 const exploded_edge *eedge; 2825 FOR_EACH_VEC_ELT_REVERSE (m_edges, i, eedge) 2826 { 2827 const exploded_node *dst_node = eedge->m_dest; 2828 const program_point &dst_point = dst_node->get_point (); 2829 const gimple *stmt = dst_point.get_stmt (); 2830 if (stmt == search_stmt) 2831 { 2832 *out_idx = i; 2833 return true; 2834 } 2835 } 2836 return false; 2837 } 2838 2839 /* Get the final exploded_node in this path, which must be non-empty. */ 2840 2841 exploded_node * 2842 exploded_path::get_final_enode () const 2843 { 2844 gcc_assert (m_edges.length () > 0); 2845 return m_edges[m_edges.length () - 1]->m_dest; 2846 } 2847 2848 /* Check state along this path, returning true if it is feasible. 2849 If OUT is non-NULL, and the path is infeasible, write a new 2850 feasibility_problem to *OUT. */ 2851 2852 bool 2853 exploded_path::feasible_p (logger *logger, feasibility_problem **out) const 2854 { 2855 LOG_SCOPE (logger); 2856 2857 /* Traverse the path, updating this model. */ 2858 region_model model; 2859 for (unsigned i = 0; i < m_edges.length (); i++) 2860 { 2861 const exploded_edge *eedge = m_edges[i]; 2862 if (logger) 2863 logger->log ("considering edge %i: EN:%i -> EN:%i", 2864 i, 2865 eedge->m_src->m_index, 2866 eedge->m_dest->m_index); 2867 const exploded_node &src_enode = *eedge->m_src; 2868 const program_point &src_point = src_enode.get_point (); 2869 if (logger) 2870 { 2871 logger->start_log_line (); 2872 src_point.print (logger->get_printer (), format (false)); 2873 logger->end_log_line (); 2874 } 2875 2876 if (const gimple *stmt = src_point.get_stmt ()) 2877 { 2878 /* Update cfun and input_location in case of ICE: make it easier to 2879 track down which source construct we're failing to handle. */ 2880 auto_cfun sentinel (src_point.get_function ()); 2881 input_location = stmt->location; 2882 2883 if (const gassign *assign = dyn_cast <const gassign *> (stmt)) 2884 model.on_assignment (assign, NULL); 2885 else if (const greturn *return_ = dyn_cast <const greturn *> (stmt)) 2886 model.on_return (return_, NULL); 2887 } 2888 2889 const superedge *sedge = eedge->m_sedge; 2890 if (sedge) 2891 { 2892 if (logger) 2893 logger->log (" sedge: SN:%i -> SN:%i %s", 2894 sedge->m_src->m_index, 2895 sedge->m_dest->m_index, 2896 sedge->get_description (false)); 2897 2898 const gimple *last_stmt = src_point.get_supernode ()->get_last_stmt (); 2899 if (!model.maybe_update_for_edge (*sedge, last_stmt, NULL)) 2900 { 2901 if (logger) 2902 { 2903 logger->log ("rejecting due to region model"); 2904 model.dump_to_pp (logger->get_printer (), false); 2905 } 2906 if (out) 2907 *out = new feasibility_problem (i, model, *eedge, last_stmt); 2908 return false; 2909 } 2910 } 2911 else 2912 { 2913 /* Special-case the initial eedge from the origin node to the 2914 initial function by pushing a frame for it. */ 2915 if (i == 0) 2916 { 2917 gcc_assert (eedge->m_src->m_index == 0); 2918 gcc_assert (src_point.get_kind () == PK_ORIGIN); 2919 gcc_assert (eedge->m_dest->get_point ().get_kind () 2920 == PK_BEFORE_SUPERNODE); 2921 function *fun = eedge->m_dest->get_function (); 2922 gcc_assert (fun); 2923 model.push_frame (fun, NULL, NULL); 2924 if (logger) 2925 logger->log (" pushing frame for %qD", fun->decl); 2926 } 2927 else if (eedge->m_custom_info) 2928 eedge->m_custom_info->update_model (&model, *eedge); 2929 } 2930 2931 /* Handle phi nodes on an edge leaving a PK_BEFORE_SUPERNODE (to 2932 a PK_BEFORE_STMT, or a PK_AFTER_SUPERNODE if no stmts). 2933 This will typically not be associated with a superedge. */ 2934 if (src_point.get_from_edge ()) 2935 { 2936 const cfg_superedge *last_cfg_superedge 2937 = src_point.get_from_edge ()->dyn_cast_cfg_superedge (); 2938 if (last_cfg_superedge) 2939 { 2940 if (logger) 2941 logger->log (" update for phis"); 2942 model.update_for_phis (src_enode.get_supernode (), 2943 last_cfg_superedge, 2944 NULL); 2945 } 2946 } 2947 2948 if (logger) 2949 { 2950 logger->log ("state after edge %i: EN:%i -> EN:%i", 2951 i, 2952 eedge->m_src->m_index, 2953 eedge->m_dest->m_index); 2954 logger->start_log_line (); 2955 model.dump_to_pp (logger->get_printer (), true); 2956 logger->end_log_line (); 2957 } 2958 } 2959 2960 return true; 2961 } 2962 2963 /* Dump this path in multiline form to PP. */ 2964 2965 void 2966 exploded_path::dump_to_pp (pretty_printer *pp) const 2967 { 2968 for (unsigned i = 0; i < m_edges.length (); i++) 2969 { 2970 const exploded_edge *eedge = m_edges[i]; 2971 pp_printf (pp, "m_edges[%i]: EN %i -> EN %i", 2972 i, 2973 eedge->m_src->m_index, 2974 eedge->m_dest->m_index); 2975 pp_newline (pp); 2976 } 2977 } 2978 2979 /* Dump this path in multiline form to FP. */ 2980 2981 void 2982 exploded_path::dump (FILE *fp) const 2983 { 2984 pretty_printer pp; 2985 pp_format_decoder (&pp) = default_tree_printer; 2986 pp_show_color (&pp) = pp_show_color (global_dc->printer); 2987 pp.buffer->stream = fp; 2988 dump_to_pp (&pp); 2989 pp_flush (&pp); 2990 } 2991 2992 /* Dump this path in multiline form to stderr. */ 2993 2994 DEBUG_FUNCTION void 2995 exploded_path::dump () const 2996 { 2997 dump (stderr); 2998 } 2999 3000 /* A family of cluster subclasses for use when generating .dot output for 3001 exploded graphs (-fdump-analyzer-exploded-graph), for grouping the 3002 enodes into hierarchical boxes. 3003 3004 All functionless enodes appear in the top-level graph. 3005 Every (function, call_string) pair gets its own cluster. Within that 3006 cluster, each supernode gets its own cluster. 3007 3008 Hence all enodes relating to a particular function with a particular 3009 callstring will be in a cluster together; all enodes for the same 3010 function but with a different callstring will be in a different 3011 cluster. */ 3012 3013 /* Base class of cluster for clustering exploded_node instances in .dot 3014 output, based on various subclass-specific criteria. */ 3015 3016 class exploded_cluster : public cluster<eg_traits> 3017 { 3018 }; 3019 3020 /* Cluster containing all exploded_node instances for one supernode. */ 3021 3022 class supernode_cluster : public exploded_cluster 3023 { 3024 public: 3025 supernode_cluster (const supernode *supernode) : m_supernode (supernode) {} 3026 3027 // TODO: dtor? 3028 3029 void dump_dot (graphviz_out *gv, const dump_args_t &args) const FINAL OVERRIDE 3030 { 3031 gv->println ("subgraph \"cluster_supernode_%p\" {", 3032 (const void *)this); 3033 gv->indent (); 3034 gv->println ("style=\"dashed\";"); 3035 gv->println ("label=\"SN: %i (bb: %i)\";", 3036 m_supernode->m_index, m_supernode->m_bb->index); 3037 3038 int i; 3039 exploded_node *enode; 3040 FOR_EACH_VEC_ELT (m_enodes, i, enode) 3041 enode->dump_dot (gv, args); 3042 3043 /* Terminate subgraph. */ 3044 gv->outdent (); 3045 gv->println ("}"); 3046 } 3047 3048 void add_node (exploded_node *en) FINAL OVERRIDE 3049 { 3050 m_enodes.safe_push (en); 3051 } 3052 3053 private: 3054 const supernode *m_supernode; 3055 auto_vec <exploded_node *> m_enodes; 3056 }; 3057 3058 /* Cluster containing all supernode_cluster instances for one 3059 (function, call_string) pair. */ 3060 3061 class function_call_string_cluster : public exploded_cluster 3062 { 3063 public: 3064 function_call_string_cluster (function *fun, call_string cs) 3065 : m_fun (fun), m_cs (cs) {} 3066 3067 ~function_call_string_cluster () 3068 { 3069 for (map_t::iterator iter = m_map.begin (); 3070 iter != m_map.end (); 3071 ++iter) 3072 delete (*iter).second; 3073 } 3074 3075 void dump_dot (graphviz_out *gv, const dump_args_t &args) const FINAL OVERRIDE 3076 { 3077 const char *funcname = function_name (m_fun); 3078 3079 gv->println ("subgraph \"cluster_function_%p\" {", (const void *)this); 3080 gv->indent (); 3081 gv->write_indent (); 3082 gv->print ("label=\"call string: "); 3083 m_cs.print (gv->get_pp ()); 3084 gv->print (" function: %s \";", funcname); 3085 gv->print ("\n"); 3086 3087 for (map_t::iterator iter = m_map.begin (); 3088 iter != m_map.end (); 3089 ++iter) 3090 (*iter).second->dump_dot (gv, args); 3091 3092 /* Terminate subgraph. */ 3093 gv->outdent (); 3094 gv->println ("}"); 3095 } 3096 3097 void add_node (exploded_node *en) FINAL OVERRIDE 3098 { 3099 const supernode *supernode = en->get_supernode (); 3100 gcc_assert (supernode); 3101 supernode_cluster **slot = m_map.get (supernode); 3102 if (slot) 3103 (*slot)->add_node (en); 3104 else 3105 { 3106 supernode_cluster *child = new supernode_cluster (supernode); 3107 m_map.put (supernode, child); 3108 child->add_node (en); 3109 } 3110 } 3111 3112 private: 3113 function *m_fun; 3114 call_string m_cs; 3115 typedef ordered_hash_map<const supernode *, supernode_cluster *> map_t; 3116 map_t m_map; 3117 }; 3118 3119 /* Keys for root_cluster. */ 3120 3121 struct function_call_string 3122 { 3123 function_call_string (function *fun, call_string cs) 3124 : m_fun (fun), m_cs (cs) 3125 { 3126 gcc_assert (fun); 3127 } 3128 3129 function *m_fun; 3130 call_string m_cs; 3131 }; 3132 3133 } // namespace ana 3134 3135 template <> struct default_hash_traits<function_call_string> 3136 : public pod_hash_traits<function_call_string> 3137 { 3138 static const bool empty_zero_p = false; 3139 }; 3140 3141 template <> 3142 inline hashval_t 3143 pod_hash_traits<function_call_string>::hash (value_type v) 3144 { 3145 return pointer_hash <function>::hash (v.m_fun) ^ v.m_cs.hash (); 3146 } 3147 3148 template <> 3149 inline bool 3150 pod_hash_traits<function_call_string>::equal (const value_type &existing, 3151 const value_type &candidate) 3152 { 3153 return existing.m_fun == candidate.m_fun && existing.m_cs == candidate.m_cs; 3154 } 3155 template <> 3156 inline void 3157 pod_hash_traits<function_call_string>::mark_deleted (value_type &v) 3158 { 3159 v.m_fun = reinterpret_cast<function *> (1); 3160 } 3161 template <> 3162 inline void 3163 pod_hash_traits<function_call_string>::mark_empty (value_type &v) 3164 { 3165 v.m_fun = NULL; 3166 } 3167 template <> 3168 inline bool 3169 pod_hash_traits<function_call_string>::is_deleted (value_type v) 3170 { 3171 return v.m_fun == reinterpret_cast<function *> (1); 3172 } 3173 template <> 3174 inline bool 3175 pod_hash_traits<function_call_string>::is_empty (value_type v) 3176 { 3177 return v.m_fun == NULL; 3178 } 3179 3180 namespace ana { 3181 3182 /* Top-level cluster for generating .dot output for exploded graphs, 3183 handling the functionless nodes, and grouping the remaining nodes by 3184 callstring. */ 3185 3186 class root_cluster : public exploded_cluster 3187 { 3188 public: 3189 ~root_cluster () 3190 { 3191 for (map_t::iterator iter = m_map.begin (); 3192 iter != m_map.end (); 3193 ++iter) 3194 delete (*iter).second; 3195 } 3196 3197 void dump_dot (graphviz_out *gv, const dump_args_t &args) const FINAL OVERRIDE 3198 { 3199 int i; 3200 exploded_node *enode; 3201 FOR_EACH_VEC_ELT (m_functionless_enodes, i, enode) 3202 enode->dump_dot (gv, args); 3203 3204 for (map_t::iterator iter = m_map.begin (); 3205 iter != m_map.end (); 3206 ++iter) 3207 (*iter).second->dump_dot (gv, args); 3208 } 3209 3210 void add_node (exploded_node *en) FINAL OVERRIDE 3211 { 3212 function *fun = en->get_function (); 3213 if (!fun) 3214 { 3215 m_functionless_enodes.safe_push (en); 3216 return; 3217 } 3218 3219 const call_string &cs = en->get_point ().get_call_string (); 3220 function_call_string key (fun, cs); 3221 function_call_string_cluster **slot = m_map.get (key); 3222 if (slot) 3223 (*slot)->add_node (en); 3224 else 3225 { 3226 function_call_string_cluster *child 3227 = new function_call_string_cluster (fun, cs); 3228 m_map.put (key, child); 3229 child->add_node (en); 3230 } 3231 } 3232 3233 private: 3234 /* This can't be an ordered_hash_map, as we can't store vec<call_string>, 3235 since it's not a POD; vec<>::quick_push has: 3236 *slot = obj; 3237 and the slot isn't initialized, so the assignment op dies when cleaning up 3238 un-inited *slot (within the truncate call). */ 3239 typedef hash_map<function_call_string, function_call_string_cluster *> map_t; 3240 map_t m_map; 3241 3242 /* This should just be the origin exploded_node. */ 3243 auto_vec <exploded_node *> m_functionless_enodes; 3244 }; 3245 3246 /* Subclass of range_label for use within 3247 exploded_graph::dump_exploded_nodes for implementing 3248 -fdump-analyzer-exploded-nodes: a label for a specific 3249 exploded_node. */ 3250 3251 class enode_label : public range_label 3252 { 3253 public: 3254 enode_label (const extrinsic_state &ext_state, 3255 exploded_node *enode) 3256 : m_ext_state (ext_state), m_enode (enode) {} 3257 3258 label_text get_text (unsigned) const FINAL OVERRIDE 3259 { 3260 pretty_printer pp; 3261 pp_format_decoder (&pp) = default_tree_printer; 3262 m_enode->get_state ().dump_to_pp (m_ext_state, true, &pp); 3263 return make_label_text (false, "EN: %i: %s", 3264 m_enode->m_index, pp_formatted_text (&pp)); 3265 } 3266 3267 private: 3268 const extrinsic_state &m_ext_state; 3269 exploded_node *m_enode; 3270 }; 3271 3272 /* Postprocessing support for dumping the exploded nodes. 3273 Handle -fdump-analyzer-exploded-nodes, 3274 -fdump-analyzer-exploded-nodes-2, and the 3275 "__analyzer_dump_exploded_nodes" builtin. */ 3276 3277 void 3278 exploded_graph::dump_exploded_nodes () const 3279 { 3280 // TODO 3281 /* Locate calls to __analyzer_dump_exploded_nodes. */ 3282 // Print how many egs there are for them? 3283 /* Better: log them as we go, and record the exploded nodes 3284 in question. */ 3285 3286 /* Show every enode. */ 3287 3288 /* Gather them by stmt, so that we can more clearly see the 3289 "hotspots" requiring numerous exploded nodes. */ 3290 3291 /* Alternatively, simply throw them all into one big rich_location 3292 and see if the label-printing will sort it out... 3293 This requires them all to be in the same source file. */ 3294 3295 if (flag_dump_analyzer_exploded_nodes) 3296 { 3297 auto_timevar tv (TV_ANALYZER_DUMP); 3298 gcc_rich_location richloc (UNKNOWN_LOCATION); 3299 unsigned i; 3300 exploded_node *enode; 3301 FOR_EACH_VEC_ELT (m_nodes, i, enode) 3302 { 3303 if (const gimple *stmt = enode->get_stmt ()) 3304 { 3305 if (get_pure_location (richloc.get_loc ()) == UNKNOWN_LOCATION) 3306 richloc.set_range (0, stmt->location, SHOW_RANGE_WITH_CARET); 3307 else 3308 richloc.add_range (stmt->location, 3309 SHOW_RANGE_WITHOUT_CARET, 3310 new enode_label (m_ext_state, enode)); 3311 } 3312 } 3313 warning_at (&richloc, 0, "%i exploded nodes", m_nodes.length ()); 3314 3315 /* Repeat the warning without all the labels, so that message is visible 3316 (the other one may well have scrolled past the terminal limit). */ 3317 warning_at (richloc.get_loc (), 0, 3318 "%i exploded nodes", m_nodes.length ()); 3319 3320 if (m_worklist.length () > 0) 3321 warning_at (richloc.get_loc (), 0, 3322 "worklist still contains %i nodes", m_worklist.length ()); 3323 } 3324 3325 /* Dump the egraph in textual form to a dump file. */ 3326 if (flag_dump_analyzer_exploded_nodes_2) 3327 { 3328 auto_timevar tv (TV_ANALYZER_DUMP); 3329 char *filename 3330 = concat (dump_base_name, ".eg.txt", NULL); 3331 FILE *outf = fopen (filename, "w"); 3332 if (!outf) 3333 error_at (UNKNOWN_LOCATION, "unable to open %qs for writing", filename); 3334 free (filename); 3335 3336 fprintf (outf, "exploded graph for %s\n", dump_base_name); 3337 fprintf (outf, " nodes: %i\n", m_nodes.length ()); 3338 fprintf (outf, " edges: %i\n", m_edges.length ()); 3339 3340 unsigned i; 3341 exploded_node *enode; 3342 FOR_EACH_VEC_ELT (m_nodes, i, enode) 3343 { 3344 fprintf (outf, "\nEN %i:\n", enode->m_index); 3345 enode->dump_succs_and_preds (outf); 3346 pretty_printer pp; 3347 enode->get_point ().print (&pp, format (true)); 3348 fprintf (outf, "%s\n", pp_formatted_text (&pp)); 3349 enode->get_state ().dump_to_file (m_ext_state, false, outf); 3350 } 3351 3352 fclose (outf); 3353 } 3354 3355 /* Dump the egraph in textual form to multiple dump files, one per enode. */ 3356 if (flag_dump_analyzer_exploded_nodes_3) 3357 { 3358 auto_timevar tv (TV_ANALYZER_DUMP); 3359 3360 unsigned i; 3361 exploded_node *enode; 3362 FOR_EACH_VEC_ELT (m_nodes, i, enode) 3363 { 3364 char *filename 3365 = xasprintf ("%s.en-%i.txt", dump_base_name, i); 3366 FILE *outf = fopen (filename, "w"); 3367 if (!outf) 3368 error_at (UNKNOWN_LOCATION, "unable to open %qs for writing", filename); 3369 free (filename); 3370 3371 fprintf (outf, "EN %i:\n", enode->m_index); 3372 enode->dump_succs_and_preds (outf); 3373 pretty_printer pp; 3374 enode->get_point ().print (&pp, format (true)); 3375 fprintf (outf, "%s\n", pp_formatted_text (&pp)); 3376 enode->get_state ().dump_to_file (m_ext_state, false, outf); 3377 3378 fclose (outf); 3379 } 3380 } 3381 3382 /* Emit a warning at any call to "__analyzer_dump_exploded_nodes", 3383 giving the number of processed exploded nodes for "before-stmt", 3384 and the IDs of processed, merger, and worklist enodes. 3385 3386 We highlight the count of *processed* enodes since this is of most 3387 interest in DejaGnu tests for ensuring that state merger has 3388 happened. 3389 3390 We don't show the count of merger and worklist enodes, as this is 3391 more of an implementation detail of the merging/worklist that we 3392 don't want to bake into our expected DejaGnu messages. */ 3393 3394 unsigned i; 3395 exploded_node *enode; 3396 hash_set<const gimple *> seen; 3397 FOR_EACH_VEC_ELT (m_nodes, i, enode) 3398 { 3399 if (enode->get_point ().get_kind () != PK_BEFORE_STMT) 3400 continue; 3401 3402 if (const gimple *stmt = enode->get_stmt ()) 3403 if (const gcall *call = dyn_cast <const gcall *> (stmt)) 3404 if (is_special_named_call_p (call, "__analyzer_dump_exploded_nodes", 3405 1)) 3406 { 3407 if (seen.contains (stmt)) 3408 continue; 3409 3410 auto_vec<exploded_node *> processed_enodes; 3411 auto_vec<exploded_node *> merger_enodes; 3412 auto_vec<exploded_node *> worklist_enodes; 3413 /* This is O(N^2). */ 3414 unsigned j; 3415 exploded_node *other_enode; 3416 FOR_EACH_VEC_ELT (m_nodes, j, other_enode) 3417 { 3418 if (other_enode->get_point ().get_kind () != PK_BEFORE_STMT) 3419 continue; 3420 if (other_enode->get_stmt () == stmt) 3421 switch (other_enode->get_status ()) 3422 { 3423 default: 3424 gcc_unreachable (); 3425 case exploded_node::STATUS_WORKLIST: 3426 worklist_enodes.safe_push (other_enode); 3427 break; 3428 case exploded_node::STATUS_PROCESSED: 3429 processed_enodes.safe_push (other_enode); 3430 break; 3431 case exploded_node::STATUS_MERGER: 3432 merger_enodes.safe_push (other_enode); 3433 break; 3434 } 3435 } 3436 3437 pretty_printer pp; 3438 pp_character (&pp, '['); 3439 print_enode_indices (&pp, processed_enodes); 3440 if (merger_enodes.length () > 0) 3441 { 3442 pp_string (&pp, "] merger(s): ["); 3443 print_enode_indices (&pp, merger_enodes); 3444 } 3445 if (worklist_enodes.length () > 0) 3446 { 3447 pp_string (&pp, "] worklist: ["); 3448 print_enode_indices (&pp, worklist_enodes); 3449 } 3450 pp_character (&pp, ']'); 3451 3452 warning_n (stmt->location, 0, processed_enodes.length (), 3453 "%i processed enode: %s", 3454 "%i processed enodes: %s", 3455 processed_enodes.length (), pp_formatted_text (&pp)); 3456 seen.add (stmt); 3457 3458 /* If the argument is non-zero, then print all of the states 3459 of the various enodes. */ 3460 tree t_arg = fold (gimple_call_arg (call, 0)); 3461 if (TREE_CODE (t_arg) != INTEGER_CST) 3462 { 3463 error_at (call->location, 3464 "integer constant required for arg 1"); 3465 return; 3466 } 3467 int i_arg = TREE_INT_CST_LOW (t_arg); 3468 if (i_arg) 3469 { 3470 exploded_node *other_enode; 3471 FOR_EACH_VEC_ELT (processed_enodes, j, other_enode) 3472 { 3473 fprintf (stderr, "%i of %i: EN %i:\n", 3474 j + 1, processed_enodes.length (), 3475 other_enode->m_index); 3476 other_enode->dump_succs_and_preds (stderr); 3477 /* Dump state. */ 3478 other_enode->get_state ().dump (m_ext_state, false); 3479 } 3480 } 3481 } 3482 } 3483 } 3484 3485 /* A collection of classes for visualizing the callgraph in .dot form 3486 (as represented in the supergraph). */ 3487 3488 /* Forward decls. */ 3489 class viz_callgraph_node; 3490 class viz_callgraph_edge; 3491 class viz_callgraph; 3492 class viz_callgraph_cluster; 3493 3494 /* Traits for using "digraph.h" to visualize the callgraph. */ 3495 3496 struct viz_callgraph_traits 3497 { 3498 typedef viz_callgraph_node node_t; 3499 typedef viz_callgraph_edge edge_t; 3500 typedef viz_callgraph graph_t; 3501 struct dump_args_t 3502 { 3503 dump_args_t (const exploded_graph *eg) : m_eg (eg) {} 3504 const exploded_graph *m_eg; 3505 }; 3506 typedef viz_callgraph_cluster cluster_t; 3507 }; 3508 3509 /* Subclass of dnode representing a function within the callgraph. */ 3510 3511 class viz_callgraph_node : public dnode<viz_callgraph_traits> 3512 { 3513 friend class viz_callgraph; 3514 3515 public: 3516 viz_callgraph_node (function *fun, int index) 3517 : m_fun (fun), m_index (index), m_num_supernodes (0), m_num_superedges (0) 3518 { 3519 gcc_assert (fun); 3520 } 3521 3522 void dump_dot (graphviz_out *gv, const dump_args_t &args) const FINAL OVERRIDE 3523 { 3524 pretty_printer *pp = gv->get_pp (); 3525 3526 dump_dot_id (pp); 3527 pp_printf (pp, " [shape=none,margin=0,style=filled,fillcolor=%s,label=<", 3528 "lightgrey"); 3529 pp_string (pp, "<TABLE BORDER=\"0\">"); 3530 pp_write_text_to_stream (pp); 3531 3532 gv->begin_trtd (); 3533 pp_printf (pp, "VCG: %i: %s", m_index, function_name (m_fun)); 3534 gv->end_tdtr (); 3535 pp_newline (pp); 3536 3537 gv->begin_trtd (); 3538 pp_printf (pp, "supernodes: %i\n", m_num_supernodes); 3539 gv->end_tdtr (); 3540 pp_newline (pp); 3541 3542 gv->begin_trtd (); 3543 pp_printf (pp, "superedges: %i\n", m_num_superedges); 3544 gv->end_tdtr (); 3545 pp_newline (pp); 3546 3547 if (args.m_eg) 3548 { 3549 unsigned i; 3550 exploded_node *enode; 3551 unsigned num_enodes = 0; 3552 FOR_EACH_VEC_ELT (args.m_eg->m_nodes, i, enode) 3553 { 3554 if (enode->get_point ().get_function () == m_fun) 3555 num_enodes++; 3556 } 3557 gv->begin_trtd (); 3558 pp_printf (pp, "enodes: %i\n", num_enodes); 3559 gv->end_tdtr (); 3560 pp_newline (pp); 3561 3562 // TODO: also show the per-callstring breakdown 3563 const exploded_graph::call_string_data_map_t *per_cs_data 3564 = args.m_eg->get_per_call_string_data (); 3565 for (exploded_graph::call_string_data_map_t::iterator iter 3566 = per_cs_data->begin (); 3567 iter != per_cs_data->end (); 3568 ++iter) 3569 { 3570 const call_string *cs = (*iter).first; 3571 //per_call_string_data *data = (*iter).second; 3572 num_enodes = 0; 3573 FOR_EACH_VEC_ELT (args.m_eg->m_nodes, i, enode) 3574 { 3575 if (enode->get_point ().get_function () == m_fun 3576 && enode->get_point ().get_call_string () == *cs) 3577 num_enodes++; 3578 } 3579 if (num_enodes > 0) 3580 { 3581 gv->begin_trtd (); 3582 cs->print (pp); 3583 pp_printf (pp, ": %i\n", num_enodes); 3584 pp_write_text_as_html_like_dot_to_stream (pp); 3585 gv->end_tdtr (); 3586 } 3587 } 3588 3589 /* Show any summaries. */ 3590 per_function_data *data = args.m_eg->get_per_function_data (m_fun); 3591 if (data) 3592 { 3593 pp_newline (pp); 3594 gv->begin_trtd (); 3595 pp_printf (pp, "summaries: %i\n", data->m_summaries.length ()); 3596 pp_write_text_as_html_like_dot_to_stream (pp); 3597 gv->end_tdtr (); 3598 } 3599 } 3600 3601 pp_string (pp, "</TABLE>>];\n\n"); 3602 pp_flush (pp); 3603 } 3604 3605 void dump_dot_id (pretty_printer *pp) const 3606 { 3607 pp_printf (pp, "vcg_%i", m_index); 3608 } 3609 3610 private: 3611 function *m_fun; 3612 int m_index; 3613 int m_num_supernodes; 3614 int m_num_superedges; 3615 }; 3616 3617 /* Subclass of dedge representing a callgraph edge. */ 3618 3619 class viz_callgraph_edge : public dedge<viz_callgraph_traits> 3620 { 3621 public: 3622 viz_callgraph_edge (viz_callgraph_node *src, viz_callgraph_node *dest) 3623 : dedge<viz_callgraph_traits> (src, dest) 3624 {} 3625 3626 void dump_dot (graphviz_out *gv, const dump_args_t &) const 3627 FINAL OVERRIDE 3628 { 3629 pretty_printer *pp = gv->get_pp (); 3630 3631 const char *style = "\"solid,bold\""; 3632 const char *color = "black"; 3633 int weight = 10; 3634 const char *constraint = "true"; 3635 3636 m_src->dump_dot_id (pp); 3637 pp_string (pp, " -> "); 3638 m_dest->dump_dot_id (pp); 3639 pp_printf (pp, 3640 (" [style=%s, color=%s, weight=%d, constraint=%s," 3641 " headlabel=\""), 3642 style, color, weight, constraint); 3643 pp_printf (pp, "\"];\n"); 3644 } 3645 }; 3646 3647 /* Subclass of digraph representing the callgraph. */ 3648 3649 class viz_callgraph : public digraph<viz_callgraph_traits> 3650 { 3651 public: 3652 viz_callgraph (const supergraph &sg); 3653 3654 viz_callgraph_node *get_vcg_node_for_function (function *fun) 3655 { 3656 return *m_map.get (fun); 3657 } 3658 3659 viz_callgraph_node *get_vcg_node_for_snode (supernode *snode) 3660 { 3661 return get_vcg_node_for_function (snode->m_fun); 3662 } 3663 3664 private: 3665 hash_map<function *, viz_callgraph_node *> m_map; 3666 }; 3667 3668 /* Placeholder subclass of cluster. */ 3669 3670 class viz_callgraph_cluster : public cluster<viz_callgraph_traits> 3671 { 3672 }; 3673 3674 /* viz_callgraph's ctor. */ 3675 3676 viz_callgraph::viz_callgraph (const supergraph &sg) 3677 { 3678 cgraph_node *node; 3679 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node) 3680 { 3681 function *fun = node->get_fun (); 3682 viz_callgraph_node *vcg_node 3683 = new viz_callgraph_node (fun, m_nodes.length ()); 3684 m_map.put (fun, vcg_node); 3685 add_node (vcg_node); 3686 } 3687 3688 unsigned i; 3689 superedge *sedge; 3690 FOR_EACH_VEC_ELT (sg.m_edges, i, sedge) 3691 { 3692 viz_callgraph_node *vcg_src = get_vcg_node_for_snode (sedge->m_src); 3693 if (vcg_src->m_fun) 3694 get_vcg_node_for_function (vcg_src->m_fun)->m_num_superedges++; 3695 if (sedge->dyn_cast_call_superedge ()) 3696 { 3697 viz_callgraph_node *vcg_dest = get_vcg_node_for_snode (sedge->m_dest); 3698 viz_callgraph_edge *vcg_edge 3699 = new viz_callgraph_edge (vcg_src, vcg_dest); 3700 add_edge (vcg_edge); 3701 } 3702 } 3703 3704 supernode *snode; 3705 FOR_EACH_VEC_ELT (sg.m_nodes, i, snode) 3706 { 3707 if (snode->m_fun) 3708 get_vcg_node_for_function (snode->m_fun)->m_num_supernodes++; 3709 } 3710 } 3711 3712 /* Dump the callgraph to FILENAME. */ 3713 3714 static void 3715 dump_callgraph (const supergraph &sg, const char *filename, 3716 const exploded_graph *eg) 3717 { 3718 FILE *outf = fopen (filename, "w"); 3719 if (!outf) 3720 return; 3721 3722 // TODO 3723 viz_callgraph vcg (sg); 3724 vcg.dump_dot (filename, NULL, viz_callgraph_traits::dump_args_t (eg)); 3725 3726 fclose (outf); 3727 } 3728 3729 /* Dump the callgraph to "<srcfile>.callgraph.dot". */ 3730 3731 static void 3732 dump_callgraph (const supergraph &sg, const exploded_graph *eg) 3733 { 3734 auto_timevar tv (TV_ANALYZER_DUMP); 3735 char *filename = concat (dump_base_name, ".callgraph.dot", NULL); 3736 dump_callgraph (sg, filename, eg); 3737 free (filename); 3738 } 3739 3740 /* Subclass of dot_annotator for implementing 3741 DUMP_BASE_NAME.supergraph-eg.dot, a post-analysis dump of the supergraph. 3742 3743 Annotate the supergraph nodes by printing the exploded nodes in concise 3744 form within them, next to their pertinent statements where appropriate, 3745 colorizing the exploded nodes based on sm-state. 3746 Also show saved diagnostics within the exploded nodes, giving information 3747 on whether they were feasible, and, if infeasible, where the problem 3748 was. */ 3749 3750 class exploded_graph_annotator : public dot_annotator 3751 { 3752 public: 3753 exploded_graph_annotator (const exploded_graph &eg) 3754 : m_eg (eg) 3755 { 3756 /* Avoid O(N^2) by prepopulating m_enodes_per_snodes. */ 3757 unsigned i; 3758 supernode *snode; 3759 FOR_EACH_VEC_ELT (eg.get_supergraph ().m_nodes, i, snode) 3760 m_enodes_per_snodes.safe_push (new auto_vec <exploded_node *> ()); 3761 exploded_node *enode; 3762 FOR_EACH_VEC_ELT (m_eg.m_nodes, i, enode) 3763 if (enode->get_supernode ()) 3764 m_enodes_per_snodes[enode->get_supernode ()->m_index]->safe_push (enode); 3765 } 3766 3767 /* Show exploded nodes for BEFORE_SUPERNODE points before N. */ 3768 bool add_node_annotations (graphviz_out *gv, const supernode &n, 3769 bool within_table) 3770 const FINAL OVERRIDE 3771 { 3772 if (!within_table) 3773 return false; 3774 gv->begin_tr (); 3775 pretty_printer *pp = gv->get_pp (); 3776 3777 gv->begin_td (); 3778 pp_string (pp, "BEFORE"); 3779 gv->end_td (); 3780 3781 unsigned i; 3782 exploded_node *enode; 3783 bool had_enode = false; 3784 FOR_EACH_VEC_ELT (*m_enodes_per_snodes[n.m_index], i, enode) 3785 { 3786 gcc_assert (enode->get_supernode () == &n); 3787 const program_point &point = enode->get_point (); 3788 if (point.get_kind () != PK_BEFORE_SUPERNODE) 3789 continue; 3790 print_enode (gv, enode); 3791 had_enode = true; 3792 } 3793 if (!had_enode) 3794 pp_string (pp, "<TD BGCOLOR=\"red\">UNREACHED</TD>"); 3795 pp_flush (pp); 3796 gv->end_tr (); 3797 return true; 3798 } 3799 3800 /* Show exploded nodes for STMT. */ 3801 void add_stmt_annotations (graphviz_out *gv, const gimple *stmt, 3802 bool within_row) 3803 const FINAL OVERRIDE 3804 { 3805 if (!within_row) 3806 return; 3807 pretty_printer *pp = gv->get_pp (); 3808 3809 const supernode *snode 3810 = m_eg.get_supergraph ().get_supernode_for_stmt (stmt); 3811 unsigned i; 3812 exploded_node *enode; 3813 bool had_td = false; 3814 FOR_EACH_VEC_ELT (*m_enodes_per_snodes[snode->m_index], i, enode) 3815 { 3816 const program_point &point = enode->get_point (); 3817 if (point.get_kind () != PK_BEFORE_STMT) 3818 continue; 3819 if (point.get_stmt () != stmt) 3820 continue; 3821 print_enode (gv, enode); 3822 had_td = true; 3823 } 3824 pp_flush (pp); 3825 if (!had_td) 3826 { 3827 gv->begin_td (); 3828 gv->end_td (); 3829 } 3830 } 3831 3832 /* Show exploded nodes for AFTER_SUPERNODE points after N. */ 3833 bool add_after_node_annotations (graphviz_out *gv, const supernode &n) 3834 const FINAL OVERRIDE 3835 { 3836 gv->begin_tr (); 3837 pretty_printer *pp = gv->get_pp (); 3838 3839 gv->begin_td (); 3840 pp_string (pp, "AFTER"); 3841 gv->end_td (); 3842 3843 unsigned i; 3844 exploded_node *enode; 3845 FOR_EACH_VEC_ELT (*m_enodes_per_snodes[n.m_index], i, enode) 3846 { 3847 gcc_assert (enode->get_supernode () == &n); 3848 const program_point &point = enode->get_point (); 3849 if (point.get_kind () != PK_AFTER_SUPERNODE) 3850 continue; 3851 print_enode (gv, enode); 3852 } 3853 pp_flush (pp); 3854 gv->end_tr (); 3855 return true; 3856 } 3857 3858 private: 3859 /* Concisely print a TD element for ENODE, showing the index, status, 3860 and any saved_diagnostics at the enode. Colorize it to show sm-state. 3861 3862 Ideally we'd dump ENODE's state here, hidden behind some kind of 3863 interactive disclosure method like a tooltip, so that the states 3864 can be explored without overwhelming the graph. 3865 However, I wasn't able to get graphviz/xdot to show tooltips on 3866 individual elements within a HTML-like label. */ 3867 void print_enode (graphviz_out *gv, const exploded_node *enode) const 3868 { 3869 pretty_printer *pp = gv->get_pp (); 3870 pp_printf (pp, "<TD BGCOLOR=\"%s\">", 3871 enode->get_dot_fillcolor ()); 3872 pp_printf (pp, "<TABLE BORDER=\"0\">"); 3873 gv->begin_trtd (); 3874 pp_printf (pp, "EN: %i", enode->m_index); 3875 switch (enode->get_status ()) 3876 { 3877 default: 3878 gcc_unreachable (); 3879 case exploded_node::STATUS_WORKLIST: 3880 pp_string (pp, "(W)"); 3881 break; 3882 case exploded_node::STATUS_PROCESSED: 3883 break; 3884 case exploded_node::STATUS_MERGER: 3885 pp_string (pp, "(M)"); 3886 break; 3887 } 3888 gv->end_tdtr (); 3889 /* Dump any saved_diagnostics at this enode. */ 3890 { 3891 const diagnostic_manager &dm = m_eg.get_diagnostic_manager (); 3892 for (unsigned i = 0; i < dm.get_num_diagnostics (); i++) 3893 { 3894 const saved_diagnostic *sd = dm.get_saved_diagnostic (i); 3895 if (sd->m_enode == enode) 3896 print_saved_diagnostic (gv, sd); 3897 } 3898 } 3899 pp_printf (pp, "</TABLE>"); 3900 pp_printf (pp, "</TD>"); 3901 } 3902 3903 /* Print a TABLE element for SD, showing the kind, the length of the 3904 exploded_path, whether the path was feasible, and if infeasible, 3905 what the problem was. */ 3906 void print_saved_diagnostic (graphviz_out *gv, 3907 const saved_diagnostic *sd) const 3908 { 3909 pretty_printer *pp = gv->get_pp (); 3910 gv->begin_trtd (); 3911 pp_printf (pp, "<TABLE BORDER=\"0\">"); 3912 gv->begin_tr (); 3913 pp_string (pp, "<TD BGCOLOR=\"green\">"); 3914 pp_printf (pp, "DIAGNOSTIC: %s", sd->m_d->get_kind ()); 3915 gv->end_tdtr (); 3916 gv->begin_trtd (); 3917 pp_printf (pp, "epath length: %i", sd->get_epath_length ()); 3918 gv->end_tdtr (); 3919 switch (sd->get_status ()) 3920 { 3921 default: 3922 case saved_diagnostic::STATUS_NEW: 3923 gcc_unreachable (); 3924 break; 3925 case saved_diagnostic::STATUS_INFEASIBLE_PATH: 3926 { 3927 gv->begin_trtd (); 3928 pp_printf (pp, "INFEASIBLE"); 3929 gv->end_tdtr (); 3930 const feasibility_problem *p = sd->get_feasibility_problem (); 3931 gcc_assert (p); 3932 gv->begin_trtd (); 3933 pp_printf (pp, "at eedge %i: EN:%i -> EN:%i", 3934 p->m_eedge_idx, 3935 p->m_eedge.m_src->m_index, 3936 p->m_eedge.m_dest->m_index); 3937 pp_write_text_as_html_like_dot_to_stream (pp); 3938 gv->end_tdtr (); 3939 gv->begin_trtd (); 3940 p->m_eedge.m_sedge->dump (pp); 3941 pp_write_text_as_html_like_dot_to_stream (pp); 3942 gv->end_tdtr (); 3943 gv->begin_trtd (); 3944 pp_gimple_stmt_1 (pp, p->m_last_stmt, 0, (dump_flags_t)0); 3945 pp_write_text_as_html_like_dot_to_stream (pp); 3946 gv->end_tdtr (); 3947 /* Ideally we'd print p->m_model here; see the notes above about 3948 tooltips. */ 3949 } 3950 break; 3951 case saved_diagnostic::STATUS_FEASIBLE_PATH: 3952 gv->begin_trtd (); 3953 pp_printf (pp, "FEASIBLE"); 3954 gv->end_tdtr (); 3955 break; 3956 } 3957 pp_printf (pp, "</TABLE>"); 3958 gv->end_tdtr (); 3959 } 3960 3961 const exploded_graph &m_eg; 3962 auto_delete_vec<auto_vec <exploded_node *> > m_enodes_per_snodes; 3963 }; 3964 3965 /* Run the analysis "engine". */ 3966 3967 void 3968 impl_run_checkers (logger *logger) 3969 { 3970 LOG_SCOPE (logger); 3971 3972 /* If using LTO, ensure that the cgraph nodes have function bodies. */ 3973 cgraph_node *node; 3974 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node) 3975 node->get_untransformed_body (); 3976 3977 /* Create the supergraph. */ 3978 supergraph sg (logger); 3979 3980 state_purge_map *purge_map = NULL; 3981 3982 if (flag_analyzer_state_purge) 3983 purge_map = new state_purge_map (sg, logger); 3984 3985 if (flag_dump_analyzer_supergraph) 3986 { 3987 /* Dump supergraph pre-analysis. */ 3988 auto_timevar tv (TV_ANALYZER_DUMP); 3989 char *filename = concat (dump_base_name, ".supergraph.dot", NULL); 3990 supergraph::dump_args_t args ((enum supergraph_dot_flags)0, NULL); 3991 sg.dump_dot (filename, args); 3992 free (filename); 3993 } 3994 3995 if (flag_dump_analyzer_state_purge) 3996 { 3997 auto_timevar tv (TV_ANALYZER_DUMP); 3998 state_purge_annotator a (purge_map); 3999 char *filename = concat (dump_base_name, ".state-purge.dot", NULL); 4000 supergraph::dump_args_t args ((enum supergraph_dot_flags)0, &a); 4001 sg.dump_dot (filename, args); 4002 free (filename); 4003 } 4004 4005 auto_delete_vec <state_machine> checkers; 4006 make_checkers (checkers, logger); 4007 4008 if (logger) 4009 { 4010 int i; 4011 state_machine *sm; 4012 FOR_EACH_VEC_ELT (checkers, i, sm) 4013 logger->log ("checkers[%i]: %s", i, sm->get_name ()); 4014 } 4015 4016 /* Extrinsic state shared by nodes in the graph. */ 4017 const extrinsic_state ext_state (checkers); 4018 4019 const analysis_plan plan (sg, logger); 4020 4021 /* The exploded graph. */ 4022 exploded_graph eg (sg, logger, ext_state, purge_map, plan, 4023 analyzer_verbosity); 4024 4025 /* Add entrypoints to the graph for externally-callable functions. */ 4026 eg.build_initial_worklist (); 4027 4028 /* Now process the worklist, exploring the <point, state> graph. */ 4029 eg.process_worklist (); 4030 4031 if (flag_dump_analyzer_exploded_graph) 4032 { 4033 auto_timevar tv (TV_ANALYZER_DUMP); 4034 char *filename 4035 = concat (dump_base_name, ".eg.dot", NULL); 4036 exploded_graph::dump_args_t args (eg); 4037 root_cluster c; 4038 eg.dump_dot (filename, &c, args); 4039 free (filename); 4040 } 4041 4042 /* Now emit any saved diagnostics. */ 4043 eg.get_diagnostic_manager ().emit_saved_diagnostics (eg); 4044 4045 eg.dump_exploded_nodes (); 4046 4047 eg.log_stats (); 4048 4049 if (flag_dump_analyzer_callgraph) 4050 dump_callgraph (sg, &eg); 4051 4052 if (flag_dump_analyzer_supergraph) 4053 { 4054 /* Dump post-analysis form of supergraph. */ 4055 auto_timevar tv (TV_ANALYZER_DUMP); 4056 char *filename = concat (dump_base_name, ".supergraph-eg.dot", NULL); 4057 exploded_graph_annotator a (eg); 4058 supergraph::dump_args_t args ((enum supergraph_dot_flags)0, &a); 4059 sg.dump_dot (filename, args); 4060 free (filename); 4061 } 4062 4063 delete purge_map; 4064 } 4065 4066 /* External entrypoint to the analysis "engine". 4067 Set up any dumps, then call impl_run_checkers. */ 4068 4069 void 4070 run_checkers () 4071 { 4072 /* Save input_location. */ 4073 location_t saved_input_location = input_location; 4074 4075 /* Handle -fdump-analyzer and -fdump-analyzer-stderr. */ 4076 FILE *dump_fout = NULL; 4077 /* Track if we're responsible for closing dump_fout. */ 4078 bool owns_dump_fout = false; 4079 if (flag_dump_analyzer_stderr) 4080 dump_fout = stderr; 4081 else if (flag_dump_analyzer) 4082 { 4083 char *dump_filename = concat (dump_base_name, ".analyzer.txt", NULL); 4084 dump_fout = fopen (dump_filename, "w"); 4085 free (dump_filename); 4086 if (dump_fout) 4087 owns_dump_fout = true; 4088 } 4089 4090 { 4091 log_user the_logger (NULL); 4092 if (dump_fout) 4093 the_logger.set_logger (new logger (dump_fout, 0, 0, 4094 *global_dc->printer)); 4095 LOG_SCOPE (the_logger.get_logger ()); 4096 4097 impl_run_checkers (the_logger.get_logger ()); 4098 4099 /* end of lifetime of the_logger (so that dump file is closed after the 4100 various dtors run). */ 4101 } 4102 4103 if (owns_dump_fout) 4104 fclose (dump_fout); 4105 4106 /* Restore input_location. Subsequent passes may assume that input_location 4107 is some arbitrary value *not* in the block tree, which might be violated 4108 if we didn't restore it. */ 4109 input_location = saved_input_location; 4110 } 4111 4112 } // namespace ana 4113 4114 #endif /* #if ENABLE_ANALYZER */ 4115