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