1 /* Subclasses of diagnostic_path and diagnostic_event for analyzer diagnostics. 2 Copyright (C) 2019-2022 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 #ifndef GCC_ANALYZER_CHECKER_PATH_H 22 #define GCC_ANALYZER_CHECKER_PATH_H 23 24 namespace ana { 25 26 /* An enum for discriminating between the concrete subclasses of 27 checker_event. */ 28 29 enum event_kind 30 { 31 EK_DEBUG, 32 EK_CUSTOM, 33 EK_STMT, 34 EK_REGION_CREATION, 35 EK_FUNCTION_ENTRY, 36 EK_STATE_CHANGE, 37 EK_START_CFG_EDGE, 38 EK_END_CFG_EDGE, 39 EK_CALL_EDGE, 40 EK_RETURN_EDGE, 41 EK_START_CONSOLIDATED_CFG_EDGES, 42 EK_END_CONSOLIDATED_CFG_EDGES, 43 EK_SETJMP, 44 EK_REWIND_FROM_LONGJMP, 45 EK_REWIND_TO_SETJMP, 46 EK_WARNING 47 }; 48 49 extern const char *event_kind_to_string (enum event_kind ek); 50 51 /* Event subclasses. 52 53 The class hierarchy looks like this (using indentation to show 54 inheritance, and with event_kinds shown for the concrete subclasses): 55 56 diagnostic_event 57 checker_event 58 debug_event (EK_DEBUG) 59 custom_event (EK_CUSTOM) 60 precanned_custom_event 61 statement_event (EK_STMT) 62 region_creation_event (EK_REGION_CREATION) 63 function_entry_event (EK_FUNCTION_ENTRY) 64 state_change_event (EK_STATE_CHANGE) 65 superedge_event 66 cfg_edge_event 67 start_cfg_edge_event (EK_START_CFG_EDGE) 68 end_cfg_edge_event (EK_END_CFG_EDGE) 69 call_event (EK_CALL_EDGE) 70 return_edge (EK_RETURN_EDGE) 71 start_consolidated_cfg_edges_event (EK_START_CONSOLIDATED_CFG_EDGES) 72 end_consolidated_cfg_edges_event (EK_END_CONSOLIDATED_CFG_EDGES) 73 setjmp_event (EK_SETJMP) 74 rewind_event 75 rewind_from_longjmp_event (EK_REWIND_FROM_LONGJMP) 76 rewind_to_setjmp_event (EK_REWIND_TO_SETJMP) 77 warning_event (EK_WARNING). */ 78 79 /* Abstract subclass of diagnostic_event; the base class for use in 80 checker_path (the analyzer's diagnostic_path subclass). */ 81 82 class checker_event : public diagnostic_event 83 { 84 public: checker_event(enum event_kind kind,location_t loc,tree fndecl,int depth)85 checker_event (enum event_kind kind, 86 location_t loc, tree fndecl, int depth) 87 : m_kind (kind), m_loc (loc), m_fndecl (fndecl), m_depth (depth), 88 m_pending_diagnostic (NULL), m_emission_id () 89 { 90 } 91 92 /* Implementation of diagnostic_event. */ 93 get_location()94 location_t get_location () const FINAL OVERRIDE { return m_loc; } get_fndecl()95 tree get_fndecl () const FINAL OVERRIDE { return m_fndecl; } get_stack_depth()96 int get_stack_depth () const FINAL OVERRIDE { return m_depth; } 97 98 /* Additional functionality. */ 99 100 virtual void prepare_for_emission (checker_path *, 101 pending_diagnostic *pd, 102 diagnostic_event_id_t emission_id); is_call_p()103 virtual bool is_call_p () const { return false; } is_function_entry_p()104 virtual bool is_function_entry_p () const { return false; } is_return_p()105 virtual bool is_return_p () const { return false; } 106 107 /* For use with %@. */ get_id_ptr()108 const diagnostic_event_id_t *get_id_ptr () const 109 { 110 return &m_emission_id; 111 } 112 113 void dump (pretty_printer *pp) const; 114 set_location(location_t loc)115 void set_location (location_t loc) { m_loc = loc; } 116 117 public: 118 const enum event_kind m_kind; 119 protected: 120 location_t m_loc; 121 tree m_fndecl; 122 int m_depth; 123 pending_diagnostic *m_pending_diagnostic; 124 diagnostic_event_id_t m_emission_id; // only set once all pruning has occurred 125 }; 126 127 /* A concrete event subclass for a purely textual event, for use in 128 debugging path creation and filtering. */ 129 130 class debug_event : public checker_event 131 { 132 public: debug_event(location_t loc,tree fndecl,int depth,const char * desc)133 debug_event (location_t loc, tree fndecl, int depth, 134 const char *desc) 135 : checker_event (EK_DEBUG, loc, fndecl, depth), 136 m_desc (xstrdup (desc)) 137 { 138 } ~debug_event()139 ~debug_event () 140 { 141 free (m_desc); 142 } 143 144 label_text get_desc (bool) const FINAL OVERRIDE; 145 146 private: 147 char *m_desc; 148 }; 149 150 /* An abstract event subclass for custom events. These are not filtered, 151 as they are likely to be pertinent to the diagnostic. */ 152 153 class custom_event : public checker_event 154 { 155 protected: custom_event(location_t loc,tree fndecl,int depth)156 custom_event (location_t loc, tree fndecl, int depth) 157 : checker_event (EK_CUSTOM, loc, fndecl, depth) 158 { 159 } 160 }; 161 162 /* A concrete custom_event subclass with a precanned message. */ 163 164 class precanned_custom_event : public custom_event 165 { 166 public: precanned_custom_event(location_t loc,tree fndecl,int depth,const char * desc)167 precanned_custom_event (location_t loc, tree fndecl, int depth, 168 const char *desc) 169 : custom_event (loc, fndecl, depth), 170 m_desc (xstrdup (desc)) 171 { 172 } ~precanned_custom_event()173 ~precanned_custom_event () 174 { 175 free (m_desc); 176 } 177 178 label_text get_desc (bool) const FINAL OVERRIDE; 179 180 private: 181 char *m_desc; 182 }; 183 184 /* A concrete event subclass describing the execution of a gimple statement, 185 for use at high verbosity levels when debugging paths. */ 186 187 class statement_event : public checker_event 188 { 189 public: 190 statement_event (const gimple *stmt, tree fndecl, int depth, 191 const program_state &dst_state); 192 193 label_text get_desc (bool) const FINAL OVERRIDE; 194 195 const gimple * const m_stmt; 196 const program_state m_dst_state; 197 }; 198 199 /* A concrete event subclass describing the creation of a region that 200 is significant for a diagnostic e.g. "region created on stack here". */ 201 202 class region_creation_event : public checker_event 203 { 204 public: 205 region_creation_event (const region *reg, 206 location_t loc, tree fndecl, int depth); 207 208 label_text get_desc (bool) const FINAL OVERRIDE; 209 210 private: 211 const region *m_reg; 212 }; 213 214 /* An event subclass describing the entry to a function. */ 215 216 class function_entry_event : public checker_event 217 { 218 public: function_entry_event(location_t loc,tree fndecl,int depth)219 function_entry_event (location_t loc, tree fndecl, int depth) 220 : checker_event (EK_FUNCTION_ENTRY, loc, fndecl, depth) 221 { 222 } 223 224 label_text get_desc (bool can_colorize) const FINAL OVERRIDE; 225 is_function_entry_p()226 bool is_function_entry_p () const FINAL OVERRIDE { return true; } 227 }; 228 229 /* Subclass of checker_event describing a state change. */ 230 231 class state_change_event : public checker_event 232 { 233 public: 234 state_change_event (const supernode *node, const gimple *stmt, 235 int stack_depth, 236 const state_machine &sm, 237 const svalue *sval, 238 state_machine::state_t from, 239 state_machine::state_t to, 240 const svalue *origin, 241 const program_state &dst_state); 242 243 label_text get_desc (bool can_colorize) const FINAL OVERRIDE; 244 get_dest_function()245 function *get_dest_function () const 246 { 247 return m_dst_state.get_current_function (); 248 } 249 250 const supernode *m_node; 251 const gimple *m_stmt; 252 const state_machine &m_sm; 253 const svalue *m_sval; 254 state_machine::state_t m_from; 255 state_machine::state_t m_to; 256 const svalue *m_origin; 257 program_state m_dst_state; 258 }; 259 260 /* Subclass of checker_event; parent class for subclasses that relate to 261 a superedge. */ 262 263 class superedge_event : public checker_event 264 { 265 public: 266 /* Mark this edge event as being either an interprocedural call or 267 return in which VAR is in STATE, and that this is critical to the 268 diagnostic (so that get_desc can attempt to get a better description 269 from any pending_diagnostic). */ record_critical_state(tree var,state_machine::state_t state)270 void record_critical_state (tree var, state_machine::state_t state) 271 { 272 m_var = var; 273 m_critical_state = state; 274 } 275 276 const callgraph_superedge& get_callgraph_superedge () const; 277 278 bool should_filter_p (int verbosity) const; 279 280 protected: 281 superedge_event (enum event_kind kind, const exploded_edge &eedge, 282 location_t loc, tree fndecl, int depth); 283 284 public: 285 const exploded_edge &m_eedge; 286 const superedge *m_sedge; 287 tree m_var; 288 state_machine::state_t m_critical_state; 289 }; 290 291 /* An abstract event subclass for when a CFG edge is followed; it has two 292 subclasses, representing the start of the edge and the end of the 293 edge, which come in pairs. */ 294 295 class cfg_edge_event : public superedge_event 296 { 297 public: 298 const cfg_superedge& get_cfg_superedge () const; 299 300 protected: 301 cfg_edge_event (enum event_kind kind, const exploded_edge &eedge, 302 location_t loc, tree fndecl, int depth); 303 }; 304 305 /* A concrete event subclass for the start of a CFG edge 306 e.g. "following 'false' branch...'. */ 307 308 class start_cfg_edge_event : public cfg_edge_event 309 { 310 public: start_cfg_edge_event(const exploded_edge & eedge,location_t loc,tree fndecl,int depth)311 start_cfg_edge_event (const exploded_edge &eedge, 312 location_t loc, tree fndecl, int depth) 313 : cfg_edge_event (EK_START_CFG_EDGE, eedge, loc, fndecl, depth) 314 { 315 } 316 317 label_text get_desc (bool can_colorize) const FINAL OVERRIDE; 318 319 private: 320 label_text maybe_describe_condition (bool can_colorize) const; 321 322 static label_text maybe_describe_condition (bool can_colorize, 323 tree lhs, 324 enum tree_code op, 325 tree rhs); 326 static bool should_print_expr_p (tree); 327 }; 328 329 /* A concrete event subclass for the end of a CFG edge 330 e.g. "...to here'. */ 331 332 class end_cfg_edge_event : public cfg_edge_event 333 { 334 public: end_cfg_edge_event(const exploded_edge & eedge,location_t loc,tree fndecl,int depth)335 end_cfg_edge_event (const exploded_edge &eedge, 336 location_t loc, tree fndecl, int depth) 337 : cfg_edge_event (EK_END_CFG_EDGE, eedge, loc, fndecl, depth) 338 { 339 } 340 get_desc(bool)341 label_text get_desc (bool /*can_colorize*/) const FINAL OVERRIDE 342 { 343 return label_text::borrow ("...to here"); 344 } 345 }; 346 347 /* A concrete event subclass for an interprocedural call. */ 348 349 class call_event : public superedge_event 350 { 351 public: 352 call_event (const exploded_edge &eedge, 353 location_t loc, tree fndecl, int depth); 354 355 label_text get_desc (bool can_colorize) const FINAL OVERRIDE; 356 357 bool is_call_p () const FINAL OVERRIDE; 358 359 const supernode *m_src_snode; 360 const supernode *m_dest_snode; 361 }; 362 363 /* A concrete event subclass for an interprocedural return. */ 364 365 class return_event : public superedge_event 366 { 367 public: 368 return_event (const exploded_edge &eedge, 369 location_t loc, tree fndecl, int depth); 370 371 label_text get_desc (bool can_colorize) const FINAL OVERRIDE; 372 373 bool is_return_p () const FINAL OVERRIDE; 374 375 const supernode *m_src_snode; 376 const supernode *m_dest_snode; 377 }; 378 379 /* A concrete event subclass for the start of a consolidated run of CFG 380 edges all either TRUE or FALSE e.g. "following 'false' branch...'. */ 381 382 class start_consolidated_cfg_edges_event : public checker_event 383 { 384 public: start_consolidated_cfg_edges_event(location_t loc,tree fndecl,int depth,bool edge_sense)385 start_consolidated_cfg_edges_event (location_t loc, tree fndecl, int depth, 386 bool edge_sense) 387 : checker_event (EK_START_CONSOLIDATED_CFG_EDGES, loc, fndecl, depth), 388 m_edge_sense (edge_sense) 389 { 390 } 391 392 label_text get_desc (bool can_colorize) const FINAL OVERRIDE; 393 394 private: 395 bool m_edge_sense; 396 }; 397 398 /* A concrete event subclass for the end of a consolidated run of 399 CFG edges e.g. "...to here'. */ 400 401 class end_consolidated_cfg_edges_event : public checker_event 402 { 403 public: end_consolidated_cfg_edges_event(location_t loc,tree fndecl,int depth)404 end_consolidated_cfg_edges_event (location_t loc, tree fndecl, int depth) 405 : checker_event (EK_END_CONSOLIDATED_CFG_EDGES, loc, fndecl, depth) 406 { 407 } 408 get_desc(bool)409 label_text get_desc (bool /*can_colorize*/) const FINAL OVERRIDE 410 { 411 return label_text::borrow ("...to here"); 412 } 413 }; 414 415 /* A concrete event subclass for a setjmp or sigsetjmp call. */ 416 417 class setjmp_event : public checker_event 418 { 419 public: setjmp_event(location_t loc,const exploded_node * enode,tree fndecl,int depth,const gcall * setjmp_call)420 setjmp_event (location_t loc, const exploded_node *enode, 421 tree fndecl, int depth, const gcall *setjmp_call) 422 : checker_event (EK_SETJMP, loc, fndecl, depth), 423 m_enode (enode), m_setjmp_call (setjmp_call) 424 { 425 } 426 427 label_text get_desc (bool can_colorize) const FINAL OVERRIDE; 428 429 void prepare_for_emission (checker_path *path, 430 pending_diagnostic *pd, 431 diagnostic_event_id_t emission_id) FINAL OVERRIDE; 432 433 private: 434 const exploded_node *m_enode; 435 const gcall *m_setjmp_call; 436 }; 437 438 /* An abstract event subclass for rewinding from a longjmp to a setjmp 439 (or siglongjmp to sigsetjmp). 440 441 Base class for two from/to subclasses, showing the two halves of the 442 rewind. */ 443 444 class rewind_event : public checker_event 445 { 446 public: 447 tree get_longjmp_caller () const; 448 tree get_setjmp_caller () const; get_eedge()449 const exploded_edge *get_eedge () const { return m_eedge; } 450 451 protected: 452 rewind_event (const exploded_edge *eedge, 453 enum event_kind kind, 454 location_t loc, tree fndecl, int depth, 455 const rewind_info_t *rewind_info); 456 const rewind_info_t *m_rewind_info; 457 458 private: 459 const exploded_edge *m_eedge; 460 }; 461 462 /* A concrete event subclass for rewinding from a longjmp to a setjmp, 463 showing the longjmp (or siglongjmp). */ 464 465 class rewind_from_longjmp_event : public rewind_event 466 { 467 public: rewind_from_longjmp_event(const exploded_edge * eedge,location_t loc,tree fndecl,int depth,const rewind_info_t * rewind_info)468 rewind_from_longjmp_event (const exploded_edge *eedge, 469 location_t loc, tree fndecl, int depth, 470 const rewind_info_t *rewind_info) 471 : rewind_event (eedge, EK_REWIND_FROM_LONGJMP, loc, fndecl, depth, 472 rewind_info) 473 { 474 } 475 476 label_text get_desc (bool can_colorize) const FINAL OVERRIDE; 477 }; 478 479 /* A concrete event subclass for rewinding from a longjmp to a setjmp, 480 showing the setjmp (or sigsetjmp). */ 481 482 class rewind_to_setjmp_event : public rewind_event 483 { 484 public: rewind_to_setjmp_event(const exploded_edge * eedge,location_t loc,tree fndecl,int depth,const rewind_info_t * rewind_info)485 rewind_to_setjmp_event (const exploded_edge *eedge, 486 location_t loc, tree fndecl, int depth, 487 const rewind_info_t *rewind_info) 488 : rewind_event (eedge, EK_REWIND_TO_SETJMP, loc, fndecl, depth, 489 rewind_info) 490 { 491 } 492 493 label_text get_desc (bool can_colorize) const FINAL OVERRIDE; 494 495 void prepare_for_emission (checker_path *path, 496 pending_diagnostic *pd, 497 diagnostic_event_id_t emission_id) FINAL OVERRIDE; 498 499 private: 500 diagnostic_event_id_t m_original_setjmp_event_id; 501 }; 502 503 /* Concrete subclass of checker_event for use at the end of a path: 504 a repeat of the warning message at the end of the path (perhaps with 505 references to pertinent events that occurred on the way), at the point 506 where the problem occurs. */ 507 508 class warning_event : public checker_event 509 { 510 public: warning_event(location_t loc,tree fndecl,int depth,const state_machine * sm,tree var,state_machine::state_t state)511 warning_event (location_t loc, tree fndecl, int depth, 512 const state_machine *sm, 513 tree var, state_machine::state_t state) 514 : checker_event (EK_WARNING, loc, fndecl, depth), 515 m_sm (sm), m_var (var), m_state (state) 516 { 517 } 518 519 label_text get_desc (bool can_colorize) const FINAL OVERRIDE; 520 521 private: 522 const state_machine *m_sm; 523 tree m_var; 524 state_machine::state_t m_state; 525 }; 526 527 /* Subclass of diagnostic_path for analyzer diagnostics. */ 528 529 class checker_path : public diagnostic_path 530 { 531 public: checker_path()532 checker_path () : diagnostic_path () {} 533 534 /* Implementation of diagnostic_path vfuncs. */ 535 num_events()536 unsigned num_events () const FINAL OVERRIDE 537 { 538 return m_events.length (); 539 } 540 get_event(int idx)541 const diagnostic_event & get_event (int idx) const FINAL OVERRIDE 542 { 543 return *m_events[idx]; 544 } 545 get_checker_event(int idx)546 checker_event *get_checker_event (int idx) 547 { 548 return m_events[idx]; 549 } 550 551 void dump (pretty_printer *pp) const; 552 void debug () const; 553 554 void maybe_log (logger *logger, const char *desc) const; 555 add_event(checker_event * event)556 void add_event (checker_event *event) 557 { 558 m_events.safe_push (event); 559 } 560 delete_event(int idx)561 void delete_event (int idx) 562 { 563 checker_event *event = m_events[idx]; 564 m_events.ordered_remove (idx); 565 delete event; 566 } 567 delete_events(unsigned start_idx,unsigned len)568 void delete_events (unsigned start_idx, unsigned len) 569 { 570 for (unsigned i = start_idx; i < start_idx + len; i++) 571 delete m_events[i]; 572 m_events.block_remove (start_idx, len); 573 } 574 replace_event(unsigned idx,checker_event * new_event)575 void replace_event (unsigned idx, checker_event *new_event) 576 { 577 delete m_events[idx]; 578 m_events[idx] = new_event; 579 } 580 581 void add_region_creation_event (const region *reg, 582 location_t loc, 583 tree fndecl, int depth); 584 585 void add_final_event (const state_machine *sm, 586 const exploded_node *enode, const gimple *stmt, 587 tree var, state_machine::state_t state); 588 589 /* After all event-pruning, a hook for notifying each event what 590 its ID will be. The events are notified in order, allowing 591 for later events to refer to the IDs of earlier events in 592 their descriptions. */ prepare_for_emission(pending_diagnostic * pd)593 void prepare_for_emission (pending_diagnostic *pd) 594 { 595 checker_event *e; 596 int i; 597 FOR_EACH_VEC_ELT (m_events, i, e) 598 e->prepare_for_emission (this, pd, diagnostic_event_id_t (i)); 599 } 600 601 void fixup_locations (pending_diagnostic *pd); 602 record_setjmp_event(const exploded_node * enode,diagnostic_event_id_t setjmp_emission_id)603 void record_setjmp_event (const exploded_node *enode, 604 diagnostic_event_id_t setjmp_emission_id) 605 { 606 m_setjmp_event_ids.put (enode, setjmp_emission_id); 607 } 608 get_setjmp_event(const exploded_node * enode,diagnostic_event_id_t * out_emission_id)609 bool get_setjmp_event (const exploded_node *enode, 610 diagnostic_event_id_t *out_emission_id) 611 { 612 if (diagnostic_event_id_t *emission_id = m_setjmp_event_ids.get (enode)) 613 { 614 *out_emission_id = *emission_id; 615 return true; 616 } 617 return false; 618 } 619 620 bool cfg_edge_pair_at_p (unsigned idx) const; 621 622 private: 623 DISABLE_COPY_AND_ASSIGN(checker_path); 624 625 /* The events that have occurred along this path. */ 626 auto_delete_vec<checker_event> m_events; 627 628 /* During prepare_for_emission (and after), the setjmp_event for each 629 exploded_node *, so that rewind events can refer to them in their 630 descriptions. */ 631 hash_map <const exploded_node *, diagnostic_event_id_t> m_setjmp_event_ids; 632 }; 633 634 } // namespace ana 635 636 #endif /* GCC_ANALYZER_CHECKER_PATH_H */ 637