/* Classes for representing the state of interest at a given path of analysis. Copyright (C) 2019-2020 Free Software Foundation, Inc. Contributed by David Malcolm . This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ #ifndef GCC_ANALYZER_PROGRAM_STATE_H #define GCC_ANALYZER_PROGRAM_STATE_H namespace ana { /* Data shared by all program_state instances. */ class extrinsic_state { public: extrinsic_state (auto_delete_vec &checkers) : m_checkers (checkers) { } const state_machine &get_sm (int idx) const { return *m_checkers[idx]; } const char *get_name (int idx) const { return m_checkers[idx]->get_name (); } unsigned get_num_checkers () const { return m_checkers.length (); } void dump_to_pp (pretty_printer *pp) const; void dump_to_file (FILE *outf) const; void dump () const; private: /* The state machines. */ auto_delete_vec &m_checkers; }; } // namespace ana template <> struct default_hash_traits : public pod_hash_traits { static const bool empty_zero_p = false; }; template <> inline hashval_t pod_hash_traits::hash (value_type v) { return v.as_int (); } template <> inline bool pod_hash_traits::equal (const value_type &existing, const value_type &candidate) { return existing == candidate; } template <> inline void pod_hash_traits::mark_deleted (value_type &v) { v = svalue_id::from_int (-2); } template <> inline void pod_hash_traits::mark_empty (value_type &v) { v = svalue_id::null (); } template <> inline bool pod_hash_traits::is_deleted (value_type v) { return v.as_int () == -2; } template <> inline bool pod_hash_traits::is_empty (value_type v) { return v.null_p (); } namespace ana { /* Map from svalue_id to state machine state, also capturing the origin of each state. */ class sm_state_map { public: /* An entry in the hash_map. */ struct entry_t { /* Default ctor needed by hash_map::empty. */ entry_t () : m_state (0), m_origin (svalue_id::null ()) { } entry_t (state_machine::state_t state, svalue_id origin) : m_state (state), m_origin (origin) {} bool operator== (const entry_t &other) const { return (m_state == other.m_state && m_origin == other.m_origin); } bool operator!= (const entry_t &other) const { return !(*this == other); } state_machine::state_t m_state; svalue_id m_origin; }; typedef hash_map map_t; typedef map_t::iterator iterator_t; sm_state_map (); sm_state_map *clone () const; sm_state_map * clone_with_remapping (const one_way_svalue_id_map &id_map) const; void print (const state_machine &sm, const region_model *model, pretty_printer *pp) const; void dump (const state_machine &sm) const; bool is_empty_p () const; hashval_t hash () const; bool operator== (const sm_state_map &other) const; bool operator!= (const sm_state_map &other) const { return !(*this == other); } state_machine::state_t get_state (svalue_id sid) const; svalue_id get_origin (svalue_id sid) const; void set_state (region_model *model, svalue_id sid, state_machine::state_t state, svalue_id origin); bool set_state (const equiv_class &ec, state_machine::state_t state, svalue_id origin); bool impl_set_state (svalue_id sid, state_machine::state_t state, svalue_id origin); void set_global_state (state_machine::state_t state); state_machine::state_t get_global_state () const; void purge_for_unknown_fncall (const exploded_graph &eg, const state_machine &sm, const gcall *call, tree fndecl, region_model *new_model, region_model_context *ctxt); void remap_svalue_ids (const svalue_id_map &map); int on_svalue_purge (const state_machine &sm, int sm_idx, svalue_id first_unused_sid, const svalue_id_map &map, impl_region_model_context *ctxt); void on_inherited_svalue (svalue_id parent_sid, svalue_id child_sid); void on_cast (svalue_id src_sid, svalue_id dst_sid); void on_unknown_change (svalue_id sid); void validate (const state_machine &sm, int num_svalues) const; iterator_t begin () const { return m_map.begin (); } iterator_t end () const { return m_map.end (); } private: map_t m_map; state_machine::state_t m_global_state; }; /* A class for representing the state of interest at a given path of analysis. Currently this is a combination of: (a) a region_model, giving: (a.1) a hierarchy of memory regions (a.2) values for the regions (a.3) inequalities between values (b) sm_state_maps per state machine, giving a sparse mapping of values to states. */ class program_state { public: program_state (const extrinsic_state &ext_state); program_state (const program_state &other); program_state& operator= (const program_state &other); #if __cplusplus >= 201103 program_state (program_state &&other); program_state& operator= (program_state &&other); // doesn't seem to be used #endif ~program_state (); hashval_t hash () const; bool operator== (const program_state &other) const; bool operator!= (const program_state &other) const { return !(*this == other); } void print (const extrinsic_state &ext_state, pretty_printer *pp) const; void dump_to_pp (const extrinsic_state &ext_state, bool summarize, pretty_printer *pp) const; void dump_to_file (const extrinsic_state &ext_state, bool summarize, FILE *outf) const; void dump (const extrinsic_state &ext_state, bool summarize) const; bool on_edge (exploded_graph &eg, const exploded_node &enode, const superedge *succ, state_change *change); program_state prune_for_point (exploded_graph &eg, const program_point &point, state_change *change) const; void remap_svalue_ids (const svalue_id_map &map); tree get_representative_tree (svalue_id sid) const; bool can_purge_p (const extrinsic_state &ext_state, svalue_id sid) { /* Don't purge vars that have non-purgeable sm state, to avoid generating false "leak" complaints. */ int i; sm_state_map *smap; FOR_EACH_VEC_ELT (m_checker_states, i, smap) { const state_machine &sm = ext_state.get_sm (i); if (!sm.can_purge_p (smap->get_state (sid))) return false; } return true; } bool can_merge_with_p (const program_state &other, const extrinsic_state &ext_state, program_state *out) const; void validate (const extrinsic_state &ext_state) const; /* TODO: lose the pointer here (const-correctness issues?). */ region_model *m_region_model; auto_delete_vec m_checker_states; /* If false, then don't attempt to explore further states along this path. For use in "handling" lvalues for tree codes we haven't yet implemented. */ bool m_valid; }; /* An abstract base class for use with for_each_state_change. */ class state_change_visitor { public: virtual ~state_change_visitor () {} /* Return true for early exit, false to keep iterating. */ virtual bool on_global_state_change (const state_machine &sm, state_machine::state_t src_sm_val, state_machine::state_t dst_sm_val) = 0; /* Return true for early exit, false to keep iterating. */ virtual bool on_state_change (const state_machine &sm, state_machine::state_t src_sm_val, state_machine::state_t dst_sm_val, tree dst_rep, svalue_id dst_origin_sid) = 0; }; extern bool for_each_state_change (const program_state &src_state, const program_state &dst_state, const extrinsic_state &ext_state, state_change_visitor *visitor); /* A class for recording "interesting" state changes. This is used for annotating edges in the GraphViz output of the exploded_graph, and for recording sm-state-changes, so that values that change aren't purged (to make it easier to generate state_change_event instances in the diagnostic_path). */ class state_change { public: struct sm_change { sm_change (int sm_idx, svalue_id new_sid, state_machine::state_t old_state, state_machine::state_t new_state) : m_sm_idx (sm_idx), m_new_sid (new_sid), m_old_state (old_state), m_new_state (new_state) {} const state_machine &get_sm (const extrinsic_state &ext_state) const { return ext_state.get_sm (m_sm_idx); } void dump (pretty_printer *pp, const extrinsic_state &ext_state) const; void remap_svalue_ids (const svalue_id_map &map); int on_svalue_purge (svalue_id first_unused_sid); void validate (const program_state &new_state, const extrinsic_state &ext_state) const; int m_sm_idx; svalue_id m_new_sid; state_machine::state_t m_old_state; state_machine::state_t m_new_state; }; state_change (); state_change (const state_change &other); void add_sm_change (int sm_idx, svalue_id new_sid, state_machine::state_t old_state, state_machine::state_t new_state); bool affects_p (svalue_id sid) const; void dump (pretty_printer *pp, const extrinsic_state &ext_state) const; void dump (const extrinsic_state &ext_state) const; void remap_svalue_ids (const svalue_id_map &map); int on_svalue_purge (svalue_id first_unused_sid); void validate (const program_state &new_state, const extrinsic_state &ext_state) const; private: auto_vec m_sm_changes; }; } // namespace ana #endif /* GCC_ANALYZER_PROGRAM_STATE_H */