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