diff options
Diffstat (limited to 'gcc/analyzer')
-rw-r--r-- | gcc/analyzer/ChangeLog | 38 | ||||
-rw-r--r-- | gcc/analyzer/ana-state-to-diagnostic-state.cc | 659 | ||||
-rw-r--r-- | gcc/analyzer/ana-state-to-diagnostic-state.h | 87 | ||||
-rw-r--r-- | gcc/analyzer/checker-event.cc | 12 | ||||
-rw-r--r-- | gcc/analyzer/checker-event.h | 7 | ||||
-rw-r--r-- | gcc/analyzer/engine.cc | 4 | ||||
-rw-r--r-- | gcc/analyzer/program-state.cc | 12 | ||||
-rw-r--r-- | gcc/analyzer/program-state.h | 15 | ||||
-rw-r--r-- | gcc/analyzer/sm-malloc.cc | 52 | ||||
-rw-r--r-- | gcc/analyzer/sm.cc | 10 | ||||
-rw-r--r-- | gcc/analyzer/sm.h | 12 |
11 files changed, 453 insertions, 455 deletions
diff --git a/gcc/analyzer/ChangeLog b/gcc/analyzer/ChangeLog index d31cbbc..a34fa13 100644 --- a/gcc/analyzer/ChangeLog +++ b/gcc/analyzer/ChangeLog @@ -1,3 +1,41 @@ +2025-07-11 David Malcolm <dmalcolm@redhat.com> + + * ana-state-to-diagnostic-state.cc: Reimplement, replacing + XML-based implementation with one based on state graphs. + * ana-state-to-diagnostic-state.h: Likewise. + * checker-event.cc: Replace include of "xml.h" with include of + "diagnostic-state-graphs.h". + (checker_event::maybe_make_xml_state): Replace with... + (checker_event::maybe_make_diagnostic_state_graph): ...this. + * checker-event.h: Add include of "diagnostic-digraphs.h". + (checker_event::maybe_make_xml_state): Replace decl with... + (checker_event::maybe_make_diagnostic_state_graph): ...this. + * engine.cc (exploded_node::on_stmt_pre): Replace + "_analyzer_dump_xml" with "__analyzer_dump_sarif". + * program-state.cc: Replace include of "diagnostic-state.h" with + "diagnostic-state-graphs.h". + (program_state::dump_dot): Port from XML to state graphs. + * program-state.h: Drop reduntant forward decl of xml::document. + (program_state::make_xml): Replace decl with... + (program_state::make_diagnostic_state_graph): ...this. + (program_state::dump_xml_to_pp): Drop decl. + (program_state::dump_xml_to_file): Drop decl. + (program_state::dump_xml): Drop decl. + (program_state::dump_dump_sarif): New decl. + * sm-malloc.cc (get_dynalloc_state_for_state): New. + (malloc_state_machine::add_state_to_xml): Replace with... + (malloc_state_machine::add_state_to_state_graph): ...this. + * sm.cc (state_machine::add_state_to_xml): Replace with... + (state_machine::add_state_to_state_graph): ...this. + (state_machine::add_global_state_to_xml): Replace with... + (state_machine::add_global_state_to_state_graph): ...this. + * sm.h (class xml_state): Drop forward decl. + (class analyzer_state_graph): New forward decl. + (state_machine::add_state_to_xml): Replace decl with... + (state_machine::add_state_to_state_graph): ...this. + (state_machine::add_global_state_to_xml): Replace decl with... + (state_machine::add_global_state_to_state_graph): ...this. + 2025-06-30 David Malcolm <dmalcolm@redhat.com> * access-diagram.cc: Use nullptr rather than NULL where diff --git a/gcc/analyzer/ana-state-to-diagnostic-state.cc b/gcc/analyzer/ana-state-to-diagnostic-state.cc index b85a2f1..2701259 100644 --- a/gcc/analyzer/ana-state-to-diagnostic-state.cc +++ b/gcc/analyzer/ana-state-to-diagnostic-state.cc @@ -1,4 +1,4 @@ -/* Converting ana::program_state to XML state documents. +/* Creating diagnostic state graphs from ana::program_state. Copyright (C) 2025 Free Software Foundation, Inc. Contributed by David Malcolm <dmalcolm@redhat.com>. @@ -23,8 +23,8 @@ along with GCC; see the file COPYING3. If not see #define INCLUDE_SET #include "analyzer/common.h" -#include "xml.h" -#include "xml-printer.h" +#include "diagnostic-state-graphs.h" +#include "diagnostic-format-sarif.h" #include "analyzer/region-model.h" #include "analyzer/program-state.h" @@ -39,57 +39,47 @@ along with GCC; see the file COPYING3. If not see namespace ana { +using namespace ::diagnostics::state_graphs; + static void -set_wi_attr (xml::element &e, +set_wi_attr (state_node_ref state_node, const char *attr_name, const wide_int_ref &w, signop sgn) { pretty_printer pp; pp_wide_int (&pp, w, sgn); - e.set_attr (attr_name, pp_formatted_text (&pp)); + state_node.set_attr (attr_name, pp_formatted_text (&pp)); } static void -set_type_attr (xml::element &e, const_tree type) +set_type_attr (state_node_ref state_node, const_tree type) { gcc_assert (type); pretty_printer pp; pp_format_decoder (&pp) = default_tree_printer; pp_printf (&pp, "%T", type); - e.set_attr ("type", pp_formatted_text (&pp)); + state_node.set_type (pp_formatted_text (&pp)); } static void -set_bits_attr (xml::element &e, +set_bits_attr (state_node_ref state_node, bit_range bits) { pretty_printer pp; bits.dump_to_pp (&pp); - e.set_attr ("bits", pp_formatted_text (&pp)); + state_node.set_attr ("bits", pp_formatted_text (&pp)); } -static void -set_region_id_attr (xml::element &e, - const region ®) -{ - e.set_attr ("region_id", std::to_string (reg.get_id ())); -} +// class analyzer_state_graph : public diagnostics::digraphs::digraph -// class xml_state : public xml::document - -xml_state::xml_state (const program_state &state, - const extrinsic_state &ext_state) -: xml::document (), - m_state (state), +analyzer_state_graph::analyzer_state_graph (const program_state &state, + const extrinsic_state &ext_state) +: m_state (state), m_ext_state (ext_state), m_mgr (*ext_state.get_engine ()->get_model_manager ()), - m_root (nullptr) + m_next_id (0) { - auto root = std::make_unique<xml::element> ("state-diagram", false); - m_root = root.get (); - add_child (std::move (root)); - /* Find pointers to heap-allocated regions, and record their types, so that we have a user-friendly way of showing the memory (by field, rather than by byte offset). */ @@ -117,14 +107,14 @@ xml_state::xml_state (const program_state &state, for (int i = state.m_region_model->get_stack_depth () - 1; i >= 0; --i) { const frame_region *reg = state.m_region_model->get_frame_at_index (i); - get_or_create_element (*reg); + get_or_create_state_node (*reg); } /* Create bound memory. */ for (auto iter : *state.m_region_model->get_store ()) { const bool create_all = false; // "true" for verbose, for debugging - create_elements_for_binding_cluster (*iter.second, create_all); + create_state_nodes_for_binding_cluster (*iter.second, create_all); } /* TODO: Constraints. */ @@ -137,52 +127,165 @@ xml_state::xml_state (const program_state &state, { auto &sm = ext_state.get_sm (i); for (const auto &iter : *smap) - sm.add_state_to_xml (*this, *iter.first, iter.second.m_state); + sm.add_state_to_state_graph (*this, *iter.first, iter.second.m_state); if (auto s = smap->get_global_state ()) - sm.add_global_state_to_xml (*this, s); + sm.add_global_state_to_state_graph (*this, s); } } + + /* Process pending edges. */ + while (m_pending_edges.size () > 0) + { + pending_edge item = m_pending_edges.back (); + m_pending_edges.pop_back (); + + /* Ensure we have a node for the dst region. This + could lead to additional pending edges. */ + auto dst_node = get_or_create_state_node (item.m_dst_reg); + add_edge (nullptr, item.m_src_node.m_node, dst_node.m_node); + } } -xml::element & -xml_state::get_or_create_element (const region ®) +state_node_ref +analyzer_state_graph::get_or_create_state_node (const region ®) { - auto existing = m_region_to_element_map.find (®); - if (existing != m_region_to_element_map.end ()) + auto existing = m_region_to_state_node_map.find (®); + if (existing != m_region_to_state_node_map.end ()) return *existing->second; - auto &e = create_and_add_element (reg); - m_region_to_element_map[®] = &e; - return e; + auto ref = create_and_add_state_node (reg); + m_region_to_state_node_map[®] = &ref.m_node; + return ref; } -xml::element& -xml_state::create_and_add_element (const region ®) +state_node_ref +analyzer_state_graph::create_and_add_state_node (const region ®) { - auto e = create_element (reg); - xml::element &result = *e; + auto node = create_state_node (reg); + + state_node_ref result = *node; if (auto parent_reg = reg.get_parent_region ()) + if (parent_reg->get_kind () != RK_ROOT) + { + auto parent_state_node = get_or_create_state_node (*parent_reg); + parent_state_node.m_node.add_child (std::move (node)); + return result; + } + add_node (std::move (node)); + return result; +} + +std::string +analyzer_state_graph::make_node_id (const char *prefix) +{ + return std::string (prefix) + "-" + std::to_string (m_next_id++); +} + +std::string +analyzer_state_graph::make_node_id (const region ®) +{ + const char *prefix = nullptr; + switch (reg.get_kind ()) { - auto parent_element = &get_or_create_element (*parent_reg); - parent_element->add_child (std::move (e)); + case RK_ROOT: + default: + gcc_unreachable (); + break; + + case RK_GLOBALS: + return "globals"; + case RK_CODE: + return "code"; + case RK_STACK: + return "stack"; + case RK_HEAP: + return "heap"; + + case RK_FRAME: + prefix = "frame-region"; + break; + case RK_FUNCTION: + prefix = "function-region"; + break; + case RK_LABEL: + prefix = "label-region"; + break; + case RK_THREAD_LOCAL: + prefix = "thread-local-region"; + break; + case RK_SYMBOLIC: + prefix = "symbolic-region"; + break; + case RK_DECL: + prefix = "decl-region"; + break; + case RK_FIELD: + prefix = "field-region"; + break; + case RK_ELEMENT: + prefix = "element-region"; + break; + case RK_OFFSET: + prefix = "offset-region"; + break; + case RK_SIZED: + prefix = "sized-region"; + break; + case RK_CAST: + prefix = "cast-region"; + break; + case RK_HEAP_ALLOCATED: + prefix = "heap-allocated-region"; + break; + case RK_ALLOCA: + prefix = "alloca-region"; + break; + case RK_STRING: + prefix = "string-region"; + break; + case RK_BIT_RANGE: + prefix = "bit-range-region"; + break; + case RK_VAR_ARG: + prefix = "var-arg-region"; + break; + case RK_ERRNO: + prefix = "errno-region"; + break; + case RK_PRIVATE: + prefix = "private-region"; + break; + case RK_UNKNOWN: + prefix = "unknown-region"; + break; } - else - m_root->add_child (std::move (e)); - return result; + return std::string (prefix) + "-" + std::to_string (reg.get_id ()); +} + +std::unique_ptr<diagnostics::digraphs::node> +analyzer_state_graph:: +make_state_node (diagnostics::state_graphs::node_kind kind, + std::string id) +{ + auto node = std::make_unique<diagnostics::digraphs::node> (*this, std::move (id)); + state_node_ref node_ref (*node); + node_ref.set_node_kind (kind); + return node; } -std::unique_ptr<xml::element> -xml_state::make_memory_space_element (const char *label) +std::unique_ptr<diagnostics::digraphs::node> +analyzer_state_graph:: +make_memspace_state_node (const region ®, + diagnostics::state_graphs::node_kind kind) { - auto e = std::make_unique<xml::element> ("memory-space", false); - e->set_attr ("label", label); - return e; + return make_state_node (kind, make_node_id (reg)); } -std::unique_ptr<xml::element> -xml_state::create_element (const region ®) +std::unique_ptr<diagnostics::digraphs::node> +analyzer_state_graph::create_state_node (const region ®) { - std::unique_ptr<xml::element> e; + std::unique_ptr<diagnostics::digraphs::node> node; + switch (reg.get_kind ()) { default: @@ -190,110 +293,102 @@ xml_state::create_element (const region ®) case RK_FRAME: { - e = std::make_unique<xml::element> ("stack-frame", false); const frame_region &frame_reg = static_cast<const frame_region &> (reg); + + node = make_state_node (diagnostics::state_graphs::node_kind::stack_frame, + make_node_id (reg)); + node->set_logical_loc + (m_logical_loc_mgr.key_from_tree (frame_reg.get_fndecl ())); { pretty_printer pp; pp_format_decoder (&pp) = default_tree_printer; pp_printf (&pp, "%E", frame_reg.get_fndecl ()); - e->set_attr ("function", pp_formatted_text (&pp)); + node->set_attr (STATE_NODE_PREFIX, "function", + pp_formatted_text (&pp)); } } break; + case RK_GLOBALS: - e = make_memory_space_element ("Globals"); + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::globals); break; case RK_CODE: - e = make_memory_space_element ("Code"); + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::code); break; case RK_FUNCTION: - e = std::make_unique<xml::element> ("function", false); - // TODO - break; - case RK_LABEL: - e = std::make_unique<xml::element> ("label", false); + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::function); // TODO break; + case RK_STACK: - e = std::make_unique<xml::element> ("stack", false); + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::stack); break; case RK_HEAP: - e = make_memory_space_element ("Heap"); + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::heap_); break; case RK_THREAD_LOCAL: - e = make_memory_space_element ("Thread-local"); + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::thread_local_); break; case RK_ROOT: - e = std::make_unique<xml::element> ("memory-regions", false); + gcc_unreachable (); break; case RK_SYMBOLIC: - e = std::make_unique<xml::element> ("symbolic-region", false); - // TODO + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::other); break; + case RK_DECL: { - e = std::make_unique<xml::element> ("variable", false); + node = make_state_node (diagnostics::state_graphs::node_kind::variable, + make_node_id (reg)); const decl_region &decl_reg = static_cast<const decl_region &> (reg); + state_node_ref node_ref (*node); { pretty_printer pp; pp_format_decoder (&pp) = default_tree_printer; pp_printf (&pp, "%E", decl_reg.get_decl ()); - e->set_attr ("name", pp_formatted_text (&pp)); + node_ref.set_name (pp_formatted_text (&pp)); } - set_type_attr (*e, TREE_TYPE (decl_reg.get_decl ())); + set_type_attr (*node, TREE_TYPE (decl_reg.get_decl ())); } break; + case RK_FIELD: - e = std::make_unique<xml::element> ("field", false); - break; case RK_ELEMENT: - e = std::make_unique<xml::element> ("element", false); + /* These should be handled in populate_state_node_for_typed_region. */ + gcc_unreachable (); break; + + case RK_LABEL: case RK_OFFSET: - e = std::make_unique<xml::element> ("offset-region", false); - // TODO - break; case RK_SIZED: - e = std::make_unique<xml::element> ("sized-region", false); - // TODO - break; case RK_CAST: - e = std::make_unique<xml::element> ("cast-region", false); - // TODO - break; - case RK_HEAP_ALLOCATED: - e = std::make_unique<xml::element> ("heap-buffer", false); - set_attr_for_dynamic_extents (reg, *e); - break; - case RK_ALLOCA: - e = std::make_unique<xml::element> ("alloca-buffer", false); - set_attr_for_dynamic_extents (reg, *e); - break; case RK_STRING: - e = std::make_unique<xml::element> ("string-region", false); - // TODO - break; case RK_BIT_RANGE: - e = std::make_unique<xml::element> ("RK_BIT_RANGE", false); // TODO - break; case RK_VAR_ARG: - e = std::make_unique<xml::element> ("RK_VAR_ARG", false); // TODO - break; case RK_ERRNO: - e = std::make_unique<xml::element> ("errno", false); - break; case RK_PRIVATE: - e = std::make_unique<xml::element> ("RK_PRIVATE", false); // TODO - break; case RK_UNKNOWN: - e = std::make_unique<xml::element> ("RK_UNKNOWN", false); // TODO + node = make_state_node (diagnostics::state_graphs::node_kind::other, + make_node_id (reg)); break; - } - gcc_assert (e); - set_region_id_attr (*e, reg); + case RK_HEAP_ALLOCATED: + case RK_ALLOCA: + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::dynalloc_buffer); + set_attr_for_dynamic_extents (reg, *node); + break; + } + gcc_assert (node); if (reg.get_base_region () == ®) if (!reg.get_type ()) @@ -303,16 +398,17 @@ xml_state::create_element (const region ®) if (search != m_types_for_untyped_regions.end ()) { tree type_to_use = search->second; - set_type_attr (*e, type_to_use); + set_type_attr (*node, type_to_use); } } - return e; + return node; } void -xml_state::create_elements_for_binding_cluster (const binding_cluster &cluster, - bool create_all) +analyzer_state_graph:: +create_state_nodes_for_binding_cluster (const binding_cluster &cluster, + bool create_all) { /* TODO: - symbolic bindings @@ -326,12 +422,12 @@ xml_state::create_elements_for_binding_cluster (const binding_cluster &cluster, if (auto conc_key = key->dyn_cast_concrete_binding ()) conc_bindings[conc_key->get_bit_range ()] = svalue; if (const region *reg = svalue->maybe_get_region ()) - get_or_create_element (*reg); + get_or_create_state_node (*reg); } - auto &e = get_or_create_element (*cluster.get_base_region ()); + auto ref = get_or_create_state_node (*cluster.get_base_region ()); - e.add_child (create_element_for_conc_bindings (conc_bindings)); + ref.m_node.add_child (create_state_node_for_conc_bindings (conc_bindings)); const region *typed_reg = cluster.get_base_region (); if (!typed_reg->get_type ()) @@ -346,44 +442,45 @@ xml_state::create_elements_for_binding_cluster (const binding_cluster &cluster, } if (typed_reg->get_type ()) - populate_element_for_typed_region (e, - *typed_reg, - conc_bindings, - create_all); + populate_state_node_for_typed_region (ref, + *typed_reg, + conc_bindings, + create_all); else { // TODO } } -std::unique_ptr<xml::element> -xml_state::create_element_for_conc_bindings (const concrete_bindings_t &conc_bindings) +std::unique_ptr<diagnostics::digraphs::node> +analyzer_state_graph::create_state_node_for_conc_bindings (const concrete_bindings_t &conc_bindings) { - auto e = std::make_unique<xml::element> ("concrete-bindings", false); + auto node = make_state_node (diagnostics::state_graphs::node_kind::other, + make_node_id ("concrete-bindings")); for (auto iter : conc_bindings) { const bit_range bits = iter.first; const svalue *sval = iter.second; - auto binding_element - = std::make_unique<xml::element> ("binding", false); - set_bits_attr (*binding_element, bits); + auto binding_state_node + = make_state_node (diagnostics::state_graphs::node_kind::other, + make_node_id ("binding")); + set_bits_attr (*binding_state_node, bits); { pretty_printer pp; pp_format_decoder (&pp) = default_tree_printer; sval->dump_to_pp (&pp, true); - binding_element->set_attr ("value", pp_formatted_text (&pp)); - if (auto svalue_element = create_element_for_svalue (sval)) - binding_element->add_child (std::move (svalue_element)); + binding_state_node->set_attr (STATE_NODE_PREFIX, "value", + pp_formatted_text (&pp)); } - e->add_child (std::move (binding_element)); + node->add_child (std::move (binding_state_node)); } - return e; + return node; } // Try to get the bit_range of REG within its base region bool -xml_state::get_bit_range_within_base_region (const region ®, - bit_range &out) +analyzer_state_graph::get_bit_range_within_base_region (const region ®, + bit_range &out) { region_offset start_offset = reg.get_offset (&m_mgr); if (!start_offset.concrete_p ()) @@ -398,30 +495,28 @@ xml_state::get_bit_range_within_base_region (const region ®, } void -xml_state::populate_element_for_typed_region (xml::element &e, - const region ®, - const concrete_bindings_t &conc_bindings, - bool create_all) +analyzer_state_graph:: +populate_state_node_for_typed_region (state_node_ref node, + const region ®, + const concrete_bindings_t &conc_bindings, + bool create_all) { const_tree reg_type = reg.get_type (); gcc_assert (reg_type); - set_type_attr (e, reg_type); + set_type_attr (node, reg_type); bit_range bits (0, 0); if (get_bit_range_within_base_region (reg, bits)) { - set_bits_attr (e, bits); + set_bits_attr (node, bits); auto search = conc_bindings.find (bits); if (search != conc_bindings.end ()) { const svalue *bound_sval = search->second; - if (auto svalue_element = create_element_for_svalue (bound_sval)) - { - xml::printer xp (e); - xp.push_tag ("value-of-region"); - xp.append (std::move (svalue_element)); - } + node.set_json_attr ("value", bound_sval->to_json ()); + if (const region *dst_reg = bound_sval->maybe_get_region ()) + m_pending_edges.push_back ({node, *dst_reg}); } } @@ -454,22 +549,23 @@ xml_state::populate_element_for_typed_region (xml::element &e, = m_mgr.get_element_region (®, const_cast<tree> (element_type), sval_index); - if (show_child_element_for_child_region_p (*child_reg, + if (show_child_state_node_for_child_region_p (*child_reg, conc_bindings, create_all)) { - // Here "element" is in the xml sense - auto child_element - = std::make_unique<xml::element> ("element", false); - set_wi_attr (*child_element, "index", idx, UNSIGNED); - set_region_id_attr (*child_element, *child_reg); + auto child_state_node + = make_state_node + (diagnostics::state_graphs::node_kind::element, + make_node_id (*child_reg)); + set_wi_attr (*child_state_node, "index", idx, UNSIGNED); + // Recurse: gcc_assert (element_type); - populate_element_for_typed_region (*child_element, - *child_reg, - conc_bindings, - create_all); - e.add_child (std::move (child_element)); + populate_state_node_for_typed_region (*child_state_node, + *child_reg, + conc_bindings, + create_all); + node.m_node.add_child (std::move (child_state_node)); } } } @@ -485,15 +581,17 @@ xml_state::populate_element_for_typed_region (xml::element &e, const bit_range bits (0, item.m_bit_range.m_size_in_bits); const region *child_reg = m_mgr.get_bit_range (®, NULL_TREE, bits); - if (show_child_element_for_child_region_p (*child_reg, - conc_bindings, - create_all)) + if (show_child_state_node_for_child_region_p (*child_reg, + conc_bindings, + create_all)) { - auto child_element - = std::make_unique<xml::element> ("padding", false); - set_wi_attr (*child_element, "num_bits", + auto child_state_node + = make_state_node + (diagnostics::state_graphs::node_kind::padding, + make_node_id (*child_reg)); + set_wi_attr (*child_state_node, "num_bits", item.m_bit_range.m_size_in_bits, SIGNED); - e.add_child (std::move (child_element)); + node.m_node.add_child (std::move (child_state_node)); } } else @@ -501,26 +599,28 @@ xml_state::populate_element_for_typed_region (xml::element &e, const region *child_reg = m_mgr.get_field_region (®, const_cast<tree> (item.m_field)); - if (show_child_element_for_child_region_p (*child_reg, + if (show_child_state_node_for_child_region_p (*child_reg, conc_bindings, create_all)) { - auto child_element - = std::make_unique<xml::element> ("field", false); + auto child_state_node + = make_state_node + (diagnostics::state_graphs::node_kind::field, + make_node_id (*child_reg)); { pretty_printer pp; pp_format_decoder (&pp) = default_tree_printer; pp_printf (&pp, "%D", item.m_field); - child_element->set_attr ("name", - pp_formatted_text (&pp)); + child_state_node->set_attr (STATE_NODE_PREFIX, "name", + pp_formatted_text (&pp)); } - set_region_id_attr (*child_element, *child_reg); + // Recurse: - populate_element_for_typed_region (*child_element, + populate_state_node_for_typed_region (*child_state_node, *child_reg, conc_bindings, create_all); - e.add_child (std::move (child_element)); + node.m_node.add_child (std::move (child_state_node)); } } } @@ -530,7 +630,8 @@ xml_state::populate_element_for_typed_region (xml::element &e, } void -xml_state::set_attr_for_dynamic_extents (const region ®, xml::element &e) +analyzer_state_graph::set_attr_for_dynamic_extents (const region ®, + state_node_ref node_ref) { const svalue *sval = m_state.m_region_model->get_dynamic_extents (®); if (sval) @@ -541,13 +642,13 @@ xml_state::set_attr_for_dynamic_extents (const region ®, xml::element &e) pp_wide_int (&pp, wi::to_wide (cst), UNSIGNED); else sval->dump_to_pp (&pp, true); - e.set_attr ("dynamic-extents", pp_formatted_text (&pp)); + node_ref.set_attr ("dynamic-extents", pp_formatted_text (&pp)); } } bool -xml_state:: -show_child_element_for_child_region_p (const region ®, +analyzer_state_graph:: +show_child_state_node_for_child_region_p (const region ®, const concrete_bindings_t &conc_bindings, bool create_all) { @@ -569,216 +670,18 @@ show_child_element_for_child_region_p (const region ®, return false; } -std::unique_ptr<xml::element> -xml_state::create_element_for_svalue (const svalue *sval) +std::unique_ptr<diagnostics::digraphs::digraph> +program_state:: +make_diagnostic_state_graph (const extrinsic_state &ext_state) const { - if (!sval) - return nullptr; - - std::unique_ptr<xml::element> result; - switch (sval->get_kind ()) - { - default: - gcc_unreachable (); - case SK_REGION: - { - const region_svalue *region_sval = (const region_svalue *)sval; - result - = std::make_unique<xml::element> ("pointer-to-region", false); - set_region_id_attr (*result, *region_sval->get_pointee ()); - } - break; - case SK_CONSTANT: - { - const constant_svalue *constant_sval = (const constant_svalue *)sval; - result = std::make_unique<xml::element> ("constant", false); - pretty_printer pp; - pp_format_decoder (&pp) = default_tree_printer; - pp_printf (&pp, "%E", constant_sval->get_constant ()); - result->set_attr ("value", pp_formatted_text (&pp)); - } - break; - case SK_UNKNOWN: - result = std::make_unique<xml::element> ("unknown", false); - break; - case SK_POISONED: - { - const poisoned_svalue *poisoned_sval = (const poisoned_svalue *)sval; - switch (poisoned_sval->get_poison_kind ()) - { - default: - gcc_unreachable (); - case poison_kind::uninit: - result = std::make_unique<xml::element> ("uninitialized", false); - break; - case poison_kind::freed: - result = std::make_unique<xml::element> ("freed", false); - break; - case poison_kind::deleted: - result = std::make_unique<xml::element> ("deleted", false); - break; - case poison_kind::popped_stack: - result = std::make_unique<xml::element> ("popped-stack", false); - break; - } - } - break; - case SK_SETJMP: - { - //const setjmp_svalue *setjmp_sval = (const setjmp_svalue *)sval; - result = std::make_unique<xml::element> ("setjmp-buffer", false); - // TODO - } - break; - case SK_INITIAL: - { - const initial_svalue *initial_sval = (const initial_svalue *)sval; - result = std::make_unique<xml::element> ("initial-value-of", false); - set_region_id_attr (*result, *initial_sval->get_region ()); - } - break; - case SK_UNARYOP: - { - const unaryop_svalue *unaryop_sval = (const unaryop_svalue *)sval; - result = std::make_unique<xml::element> ("unary-op", false); - result->set_attr ("op", get_tree_code_name (unaryop_sval->get_op ())); - result->add_child - (create_element_for_svalue (unaryop_sval->get_arg ())); - } - break; - case SK_BINOP: - { - const binop_svalue *binop_sval = (const binop_svalue *)sval; - result = std::make_unique<xml::element> ("binary-op", false); - result->set_attr ("op", get_tree_code_name (binop_sval->get_op ())); - result->add_child (create_element_for_svalue (binop_sval->get_arg0 ())); - result->add_child (create_element_for_svalue (binop_sval->get_arg1 ())); - } - break; - case SK_SUB: - { - //const sub_svalue *sub_sval = (const sub_svalue *)sval; - result = std::make_unique<xml::element> ("subregion-value", false); - // TODO - } - break; - case SK_REPEATED: - { - const repeated_svalue *repeated_sval = (const repeated_svalue *)sval; - result = std::make_unique<xml::element> ("repeated-value", false); - result->add_child - (create_element_for_svalue (repeated_sval->get_outer_size ())); - result->add_child - (create_element_for_svalue (repeated_sval->get_inner_svalue ())); - } - break; - case SK_BITS_WITHIN: - { - const bits_within_svalue *bits_within_sval - = (const bits_within_svalue *)sval; - result = std::make_unique<xml::element> ("bits-within", false); - set_bits_attr (*result, bits_within_sval->get_bits ()); - result->add_child - (create_element_for_svalue (bits_within_sval->get_inner_svalue ())); - } - break; - case SK_UNMERGEABLE: - { - const unmergeable_svalue *unmergeable_sval - = (const unmergeable_svalue *)sval; - result = std::make_unique<xml::element> ("unmergeable", false); - result->add_child - (create_element_for_svalue (unmergeable_sval->get_arg ())); - } - break; - case SK_PLACEHOLDER: - { - const placeholder_svalue *placeholder_sval - = (const placeholder_svalue *)sval; - result = std::make_unique<xml::element> ("placeholder", false); - result->set_attr ("name", placeholder_sval->get_name ()); - } - break; - case SK_WIDENING: - { - //const widening_svalue *widening_sval = (const widening_svalue *)sval; - result = std::make_unique<xml::element> ("iterating-value", false); - // TODO - } - break; - case SK_COMPOUND: - { - //const compound_svalue *compound_sval = (const compound_svalue *)sval; - result = std::make_unique<xml::element> ("compound-value", false); - // TODO - } - break; - case SK_CONJURED: - { - //const conjured_svalue *conjured_sval = (const conjured_svalue *)sval; - result = std::make_unique<xml::element> ("conjured-value", false); - // TODO - } - break; - case SK_ASM_OUTPUT: - { - /* const asm_output_svalue *asm_output_sval - = (const asm_output_svalue *)sval; */ - result = std::make_unique<xml::element> ("asm-output", false); - // TODO - } - break; - case SK_CONST_FN_RESULT: - { - /* const const_fn_result_svalue *const_fn_result_sval - = (const const_fn_result_svalue *)sval; */ - result = std::make_unique<xml::element> ("const-fn-result", false); - // TODO - } - } - - if (result) - { - if (sval->get_type ()) - set_type_attr (*result, sval->get_type ()); - - pretty_printer pp; - pp_format_decoder (&pp) = default_tree_printer; - sval->dump_to_pp (&pp, true); - result->set_attr ("dump-text", pp_formatted_text (&pp)); - } - - return result; -} - -std::unique_ptr<xml::document> -program_state::make_xml (const extrinsic_state &ext_state) const -{ - return std::make_unique<xml_state> (*this, ext_state); -} - -void -program_state::dump_xml_to_pp (const extrinsic_state &ext_state, - pretty_printer *pp) const -{ - auto doc = make_xml (ext_state); - doc->write_as_xml (pp, 0, true); -} - -void -program_state::dump_xml_to_file (const extrinsic_state &ext_state, - FILE *outf) const -{ - pretty_printer pp; - pp.set_output_stream (outf); - dump_xml_to_pp (ext_state, &pp); - pp_flush (&pp); + return std::make_unique<analyzer_state_graph> (*this, ext_state); } void -program_state::dump_xml (const extrinsic_state &ext_state) const +program_state::dump_sarif (const extrinsic_state &ext_state) const { - dump_xml_to_file (ext_state, stderr); + auto g = make_diagnostic_state_graph (ext_state); + g->dump (); } } // namespace ana diff --git a/gcc/analyzer/ana-state-to-diagnostic-state.h b/gcc/analyzer/ana-state-to-diagnostic-state.h index bd6aa46..186e19d 100644 --- a/gcc/analyzer/ana-state-to-diagnostic-state.h +++ b/gcc/analyzer/ana-state-to-diagnostic-state.h @@ -1,4 +1,4 @@ -/* XML documents for dumping state in an easier-to-read form. +/* Creating diagnostic state graphs from ana::program_state. Copyright (C) 2025 Free Software Foundation, Inc. Contributed by David Malcolm <dmalcolm@redhat.com>. @@ -18,41 +18,52 @@ You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ -#ifndef GCC_ANALYZER_ANA_STATE_TO_XML_STATE_H -#define GCC_ANALYZER_ANA_STATE_TO_XML_STATE_H +#ifndef GCC_ANALYZER_ANA_STATE_TO_DIAGNOSTIC_STATE_H +#define GCC_ANALYZER_ANA_STATE_TO_DIAGNOSTIC_STATE_H -#include "xml.h" +#include "diagnostic-state-graphs.h" +#include "tree-logical-location.h" namespace ana { -class xml_state : public xml::document +class analyzer_state_graph : public diagnostics::digraphs::digraph { public: - xml_state (const program_state &state, - const extrinsic_state &ext_state); - - xml::element & - get_or_create_element (const region ®); + analyzer_state_graph (const program_state &state, + const extrinsic_state &ext_state); + diagnostics::state_graphs::state_node_ref + get_or_create_state_node (const region ®); private: - xml::element& - create_and_add_element (const region ®); - - static std::unique_ptr<xml::element> - make_memory_space_element (const char *label); - - std::unique_ptr<xml::element> - create_element (const region ®); + struct pending_edge + { + diagnostics::state_graphs::state_node_ref m_src_node; + const region &m_dst_reg; + }; + + diagnostics::state_graphs::state_node_ref + create_and_add_state_node (const region ®); + + std::unique_ptr<diagnostics::digraphs::node> + make_state_node (diagnostics::state_graphs::node_kind kind, + std::string id); + + std::unique_ptr<diagnostics::digraphs::node> + make_memspace_state_node (const region ®, + enum diagnostics::state_graphs::node_kind kind); + + std::unique_ptr<diagnostics::digraphs::node> + create_state_node (const region ®); /* Spatially sorted concrete bindings. */ typedef std::map<bit_range, const svalue *> concrete_bindings_t; void - create_elements_for_binding_cluster (const binding_cluster &cluster, - bool create_all); + create_state_nodes_for_binding_cluster (const binding_cluster &cluster, + bool create_all); - std::unique_ptr<xml::element> - create_element_for_conc_bindings (const concrete_bindings_t &conc_bindings); + std::unique_ptr<diagnostics::digraphs::node> + create_state_node_for_conc_bindings (const concrete_bindings_t &conc_bindings); // Try to get the bit_range of REG within its base region bool @@ -60,30 +71,36 @@ private: bit_range &out); void - populate_element_for_typed_region (xml::element &e, - const region ®, - const concrete_bindings_t &conc_bindings, - bool create_all); + populate_state_node_for_typed_region (diagnostics::state_graphs::state_node_ref, + const region ®, + const concrete_bindings_t &conc_bindings, + bool create_all); void - set_attr_for_dynamic_extents (const region ®, xml::element &e); + set_attr_for_dynamic_extents (const region ®, + diagnostics::state_graphs::state_node_ref); bool - show_child_element_for_child_region_p (const region ®, - const concrete_bindings_t &conc_bindings, - bool create_all); + show_child_state_node_for_child_region_p (const region ®, + const concrete_bindings_t &conc_bindings, + bool create_all); + + std::unique_ptr<diagnostics::digraphs::node> + create_state_node_for_svalue (const svalue *sval); - std::unique_ptr<xml::element> - create_element_for_svalue (const svalue *sval); + std::string make_node_id (const region ®); + std::string make_node_id (const char *prefix); + tree_logical_location_manager m_logical_loc_mgr; const program_state &m_state; const extrinsic_state &m_ext_state; region_model_manager &m_mgr; - xml::element *m_root; - std::map<const region *, xml::element *> m_region_to_element_map; + std::map<const region *, diagnostics::digraphs::node *> m_region_to_state_node_map; std::map<const region *, tree> m_types_for_untyped_regions; + unsigned m_next_id; + std::vector<pending_edge> m_pending_edges; }; } // namespace ana -#endif /* GCC_ANALYZER_ANA_STATE_TO_XML_STATE_H */ +#endif /* GCC_ANALYZER_ANA_STATE_TO_DIAGNOSTIC_STATE_H */ diff --git a/gcc/analyzer/checker-event.cc b/gcc/analyzer/checker-event.cc index af336df..8cc5ac2 100644 --- a/gcc/analyzer/checker-event.cc +++ b/gcc/analyzer/checker-event.cc @@ -28,7 +28,7 @@ along with GCC; see the file COPYING3. If not see #include "inlining-iterator.h" #include "tree-logical-location.h" #include "diagnostic-format-sarif.h" -#include "xml.h" +#include "diagnostic-state-graphs.h" #include "analyzer/analyzer-logging.h" #include "analyzer/sm.h" @@ -225,8 +225,8 @@ checker_event::prepare_for_emission (checker_path *path, print_desc (*pp.get ()); } -std::unique_ptr<xml::document> -checker_event::maybe_make_xml_state (bool debug) const +std::unique_ptr<diagnostics::digraphs::digraph> +checker_event::maybe_make_diagnostic_state_graph (bool debug) const { const program_state *state = get_program_state (); if (!state) @@ -235,14 +235,16 @@ checker_event::maybe_make_xml_state (bool debug) const gcc_assert (m_path); const extrinsic_state &ext_state = m_path->get_ext_state (); - auto result = state->make_xml (ext_state); + auto result = state->make_diagnostic_state_graph (ext_state); if (debug) { pretty_printer pp; text_art::theme *theme = global_dc->get_diagram_theme (); text_art::dump_to_pp (*state, theme, &pp); - result->add_comment (pp_formatted_text (&pp)); + result->set_attr (STATE_GRAPH_PREFIX, + "analyzer/program_state/", + pp_formatted_text (&pp)); } return result; diff --git a/gcc/analyzer/checker-event.h b/gcc/analyzer/checker-event.h index 7c44f1e..cf24e77 100644 --- a/gcc/analyzer/checker-event.h +++ b/gcc/analyzer/checker-event.h @@ -24,6 +24,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-logical-location.h" #include "analyzer/program-state.h" #include "analyzer/event-loc-info.h" +#include "diagnostic-digraphs.h" namespace ana { @@ -128,12 +129,12 @@ public: virtual bool is_function_entry_p () const { return false; } virtual bool is_return_p () const { return false; } + std::unique_ptr<diagnostics::digraphs::digraph> + maybe_make_diagnostic_state_graph (bool debug) const final override; + virtual const program_state * get_program_state () const { return nullptr; } - std::unique_ptr<xml::document> - maybe_make_xml_state (bool debug) const final override; - /* For use with %@. */ const diagnostic_event_id_t *get_id_ptr () const { diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc index 67024e9..745ef7e 100644 --- a/gcc/analyzer/engine.cc +++ b/gcc/analyzer/engine.cc @@ -1580,9 +1580,9 @@ exploded_node::on_stmt_pre (exploded_graph &eg, state->dump (eg.get_ext_state (), true); return; } - else if (is_special_named_call_p (call, "__analyzer_dump_xml", 0)) + else if (is_special_named_call_p (call, "__analyzer_dump_sarif", 0)) { - state->dump_xml (eg.get_ext_state ()); + state->dump_sarif (eg.get_ext_state ()); return; } else if (is_special_named_call_p (call, "__analyzer_dump_dot", 0)) diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc index c0befac..85cbe75 100644 --- a/gcc/analyzer/program-state.cc +++ b/gcc/analyzer/program-state.cc @@ -28,7 +28,7 @@ along with GCC; see the file COPYING3. If not see #include "cgraph.h" #include "digraph.h" #include "diagnostic-event-id.h" -#include "diagnostic-state.h" +#include "diagnostic-state-graphs.h" #include "graphviz.h" #include "text-art/tree-widget.h" @@ -1230,8 +1230,14 @@ program_state::make_dump_widget (const text_art::dump_widget_info &dwi) const void program_state::dump_dot (const extrinsic_state &ext_state) const { - auto doc = make_xml (ext_state); - auto graph = make_dot_graph_from_xml_state (*doc); + auto state_graph = make_diagnostic_state_graph (ext_state); + + gcc_assert (global_dc); + auto logical_loc_mgr = global_dc->get_logical_location_manager (); + gcc_assert (logical_loc_mgr); + + auto graph = diagnostics::state_graphs::make_dot_graph (*state_graph, + *logical_loc_mgr); pretty_printer pp; dot::writer w (pp); diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h index e2076c1..4278237 100644 --- a/gcc/analyzer/program-state.h +++ b/gcc/analyzer/program-state.h @@ -26,8 +26,6 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/store.h" -namespace xml { class document; } - namespace ana { /* Data shared by all program_state instances. */ @@ -248,11 +246,14 @@ public: void dump (const extrinsic_state &ext_state, bool simple) const; void dump () const; - std::unique_ptr<xml::document> make_xml (const extrinsic_state &ext_state) const; - void dump_xml_to_pp (const extrinsic_state &ext_state, pretty_printer *pp) const; - void dump_xml_to_file (const extrinsic_state &ext_state, FILE *outf) const; - void dump_xml (const extrinsic_state &ext_state) const; - void dump_dot (const extrinsic_state &ext_state) const; + std::unique_ptr<diagnostics::digraphs::digraph> + make_diagnostic_state_graph (const extrinsic_state &ext_state) const; + + void + dump_sarif (const extrinsic_state &ext_state) const; + + void + dump_dot (const extrinsic_state &ext_state) const; std::unique_ptr<json::object> to_json (const extrinsic_state &ext_state) const; diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc index 3581dbb..2a218d0 100644 --- a/gcc/analyzer/sm-malloc.cc +++ b/gcc/analyzer/sm-malloc.cc @@ -436,9 +436,9 @@ public: const extrinsic_state &ext_state) const; void - add_state_to_xml (xml_state &out_xml, - const svalue &sval, - state_machine::state_t state) const final override; + add_state_to_state_graph (analyzer_state_graph &out_state_graph, + const svalue &sval, + state_machine::state_t state) const final override; standard_deallocator_set m_free; standard_deallocator_set m_scalar_delete; @@ -2735,27 +2735,57 @@ malloc_state_machine::transition_ptr_sval_non_null (region_model *model, smap->set_state (model, new_ptr_sval, m_free.m_nonnull, nullptr, ext_state); } +static enum diagnostics::state_graphs::node_dynalloc_state +get_dynalloc_state_for_state (enum resource_state rs) +{ + switch (rs) + { + default: + gcc_unreachable (); + case RS_START: + case RS_NULL: + case RS_NON_HEAP: + case RS_STOP: + return diagnostics::state_graphs::node_dynalloc_state::unknown; + + case RS_ASSUMED_NON_NULL: + return diagnostics::state_graphs::node_dynalloc_state::nonnull; + + case RS_UNCHECKED: + return diagnostics::state_graphs::node_dynalloc_state::unchecked; + case RS_NONNULL: + return diagnostics::state_graphs::node_dynalloc_state::nonnull; + case RS_FREED: + return diagnostics::state_graphs::node_dynalloc_state::freed; + } +} + void -malloc_state_machine::add_state_to_xml (xml_state &out_xml, - const svalue &sval, - state_machine::state_t state) const +malloc_state_machine:: +add_state_to_state_graph (analyzer_state_graph &out_state_graph, + const svalue &sval, + state_machine::state_t state) const { if (const region *reg = sval.maybe_get_region ()) { - auto ®_element = out_xml.get_or_create_element (*reg); + auto reg_node = out_state_graph.get_or_create_state_node (*reg); auto alloc_state = as_a_allocation_state (state); gcc_assert (alloc_state); - reg_element.set_attr ("dynamic-alloc-state", state->get_name ()); + reg_node.set_dynalloc_state + (get_dynalloc_state_for_state (alloc_state->m_rs)); if (alloc_state->m_deallocators) { pretty_printer pp; alloc_state->m_deallocators->dump_to_pp (&pp); - reg_element.set_attr ("expected-deallocators", pp_formatted_text (&pp)); + reg_node.m_node.set_attr (STATE_NODE_PREFIX, + "expected-deallocators", + pp_formatted_text (&pp)); } if (alloc_state->m_deallocator) - reg_element.set_attr ("deallocator", - alloc_state->m_deallocator->m_name); + reg_node.m_node.set_attr (STATE_NODE_PREFIX, + "deallocator", + alloc_state->m_deallocator->m_name); } } diff --git a/gcc/analyzer/sm.cc b/gcc/analyzer/sm.cc index 840806a..c93e9c2 100644 --- a/gcc/analyzer/sm.cc +++ b/gcc/analyzer/sm.cc @@ -161,16 +161,16 @@ state_machine::to_json () const } void -state_machine::add_state_to_xml (xml_state &out_xml, - const svalue &sval, - state_machine::state_t state) const +state_machine::add_state_to_state_graph (analyzer_state_graph &out_state_graph, + const svalue &sval, + state_machine::state_t state) const { // no-op } void -state_machine::add_global_state_to_xml (xml_state &out_xml, - state_machine::state_t state) const +state_machine::add_global_state_to_state_graph (analyzer_state_graph &out_state_graph, + state_machine::state_t state) const { // no-op } diff --git a/gcc/analyzer/sm.h b/gcc/analyzer/sm.h index 6298fb6..4633fac 100644 --- a/gcc/analyzer/sm.h +++ b/gcc/analyzer/sm.h @@ -28,7 +28,7 @@ namespace ana { class state_machine; class sm_context; class pending_diagnostic; -class xml_state; +class analyzer_state_graph; extern bool any_pointer_p (tree expr); extern bool any_pointer_p (const svalue *sval); @@ -188,13 +188,13 @@ public: state_t get_start_state () const { return m_start; } virtual void - add_state_to_xml (xml_state &out_xml, - const svalue &sval, - state_machine::state_t state) const; + add_state_to_state_graph (analyzer_state_graph &out_state_graph, + const svalue &sval, + state_machine::state_t state) const; virtual void - add_global_state_to_xml (xml_state &out_xml, - state_machine::state_t state) const; + add_global_state_to_state_graph (analyzer_state_graph &out_state_graph, + state_machine::state_t state) const; protected: state_t add_state (const char *name); |