From: David Malcolm Date: Thu, 2 Oct 2025 22:19:13 +0000 (-0400) Subject: diagnostics: generalize state graph code to use json::property instances X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=r16-4193-ge4ab1f87805a65;p=thirdparty%2Fgcc.git diagnostics: generalize state graph code to use json::property instances In r16-1631-g2334d30cd8feac I added support for capturing state information from -fanalyzer in the form of embedded XML strings in SARIF output. In r16-2211-ga5d9debedd2f46 I rewrote this so the state was captured in the form of a SARIF directed graph, using various custom types. I want to add the ability to capture other kinds of graph in our SARIF output (e.g. inheritance hierarchies, CFGs, etc), so the following patch reworks the state graph handling code to minimize the use of custom types. Instead, the patch introduces various json::property types, and describes the state graph serialization in terms of instances of these properties, rather than hardcoding string attribute names in readers and writers. The custom SARIF properties live in a new "gcc/custom-sarif-properties/" directory. The "experimental-html" scheme keys "show-state-diagrams-dot-src" and "show-state-diagrams-sarif" become "show-graph-dot-src" and "show-graph-dot-src" in preparation for new kinds of graph in the output. contrib/ChangeLog: * gcc.doxy (INPUT): Add gcc/custom-sarif-properties gcc/ChangeLog: * Makefile.in (OBJS-libcommon): Add custom-sarif-properties/digraphs.o and custom-sarif-properties/state-graphs.o. Remove diagnostics/state-graphs.o. * configure: Regenerate. * configure.ac: Add custom-sarif-properties to subdir iteration. * custom-sarif-properties/digraphs.cc: New file. * custom-sarif-properties/digraphs.h: New file. * custom-sarif-properties/state-graphs.cc: New file. * custom-sarif-properties/state-graphs.h: New file. * diagnostics/diagnostics-selftests.cc (run_diagnostics_selftests): Drop call of state_graphs_cc_tests. * diagnostics/diagnostics-selftests.h (state_graphs_cc_tests): Delete decl. * diagnostics/digraphs.cc: Include "custom-sarif-properties/digraphs.h". Move include of "selftest.h" to within CHECKING_P section. (using digraph_object): New. (namespace properties): New. (diagnostics::digraphs::object::get_attr): Delete. (diagnostics::digraphs::object::set_attr): Delete. (diagnostics::digraphs::object::set_json_attr): Delete. (digraph_object::get_property): New definitions, for various property types. (digraph_object::set_property): Likewise. (digraph_object::maybe_get_property): New. (digraph_object::get_property_as_tristate): New. (digraph_object::ensure_property_bag): New. (digraph::get_graph_kind): New. (digraph::set_graph_kind): New. Add include of "custom-sarif-properties/state-graphs.h". (selftest::test_simple_graph): Rewrite to use json::property instances rather than string attribute names. (selftest::test_property_objects): New test. (selftest::digraphs_cc_tests): Call it. * diagnostics/digraphs.h: Include "tristate.h". (object::get_attr): Delete. (object::set_attr): Delete. (object::get_property): New decls. (object::set_property): New decls. (object::maybe_get_property): New. (object::get_property_as_tristate): New. (object::set_json_attr): Delete. (object::ensure_property_bag): New. (graph::get_graph_kind): New. (graph::set_graph_kind): New. * diagnostics/html-sink.cc (html_generation_options::html_generation_options): Update for field renamings. (html_generation_options::dump): Likewise. (html_builder::maybe_make_state_diagram): Likewise. (html_builder::add_graph): Show SARIF and .dot src inline, if requested. * diagnostics/html-sink.h (html_generation_options::m_show_state_diagrams_sarif): Rename to... (html_generation_options::m_show_graph_sarif): ...this. (html_generation_options::m_show_state_diagrams_dot_src): Rename to... (html_generation_options::m_show_graph_dot_src0): ...this. * diagnostics/output-spec.cc (html_scheme_handler::maybe_handle_kv): Rename keys. (html_scheme_handler::get_keys): Likewise. * diagnostics/state-graphs-to-dot.cc: : Reimplement throughout to use json::property instances found within custom_sarif_properties throughout, rather than types in diagnostics::state_graphs. * diagnostics/state-graphs.cc: Deleted file. * diagnostics/state-graphs.h: Delete almost all, except decl of diagnostics::state_graphs::make_dot_graph. * doc/invoke.texi: Update for changes to "experimental-html" sink keys. * json.cc (json::object::set_string): New. (json::object::set_integer): New. (json::object::set_bool): New. (json::object::set_array_of_string): New. * json.h: Include "label-text.h". (struct json::property): New template. (json::string_property): New. (json::integer_property): New. (json::bool_property): New. (json::json_property): New. (using json::array_of_string_property): New. (struct json::enum_traits): New. (enum_json::property): New. (json::value::dyn_cast_array): New vfunc. (json::value::dyn_cast_integer_number): New vfunc. (json::value::set_string): New. (json::value::set_integer): New. (json::value::set_bool): New. (json::value::set_array_of_string): New. (json::value::maybe_get_enum): New. (json::value::set_enum): New. (json::array::dyn_cast_array): New. (json::integer_number::dyn_cast_integer_number): New. (object::maybe_get_enum): New. (object::set_enum): New. gcc/analyzer/ChangeLog: * ana-state-to-diagnostic-state.cc: Reimplement throughout to use json::property instances found within custom_sarif_properties throughout, rather than types in diagnostics::state_graphs. * ana-state-to-diagnostic-state.h: Likewise. * checker-event.cc: Likewise. * sm-malloc.cc: Likewise. gcc/testsuite/ChangeLog: * gcc.dg/plugin/diagnostic_plugin_test_graphs.cc (report_diag_with_graphs): Port from set_attr to set_property. Signed-off-by: David Malcolm --- diff --git a/contrib/gcc.doxy b/contrib/gcc.doxy index 15952046f25..56e3845d00d 100644 --- a/contrib/gcc.doxy +++ b/contrib/gcc.doxy @@ -478,7 +478,7 @@ WARN_LOGFILE = # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = gcc gcc/analyzer gcc/diagnostics gcc/text-art +INPUT = gcc gcc/analyzer gcc/custom-sarif-properties gcc/diagnostics gcc/text-art # This tag can be used to specify the character encoding of the source files that # doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 6a9d6204c86..098bafbce53 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1852,6 +1852,8 @@ OBJS = \ # Objects in libcommon.a, potentially used by all host binaries and with # no target dependencies. OBJS-libcommon = \ + custom-sarif-properties/digraphs.o \ + custom-sarif-properties/state-graphs.o \ diagnostic-global-context.o \ diagnostics/buffering.o \ diagnostics/changes.o \ @@ -1871,7 +1873,6 @@ OBJS-libcommon = \ diagnostics/paths.o \ diagnostics/paths-output.o \ diagnostics/source-printing.o \ - diagnostics/state-graphs.o \ diagnostics/state-graphs-to-dot.o \ diagnostics/selftest-context.o \ diagnostics/selftest-logical-locations.o \ diff --git a/gcc/analyzer/ana-state-to-diagnostic-state.cc b/gcc/analyzer/ana-state-to-diagnostic-state.cc index 996538c3785..39acf26bdd5 100644 --- a/gcc/analyzer/ana-state-to-diagnostic-state.cc +++ b/gcc/analyzer/ana-state-to-diagnostic-state.cc @@ -39,38 +39,55 @@ along with GCC; see the file COPYING3. If not see namespace ana { -using namespace ::diagnostics::state_graphs; +namespace node_properties = custom_sarif_properties::state_graphs::node; static void -set_wi_attr (state_node_ref state_node, - const char *attr_name, +set_wi_attr (diagnostics::digraphs::node &state_node, + const json::string_property &property, const wide_int_ref &w, signop sgn) { pretty_printer pp; pp_wide_int (&pp, w, sgn); - state_node.set_attr (attr_name, pp_formatted_text (&pp)); + state_node.set_property (property, pp_formatted_text (&pp)); } static void -set_type_attr (state_node_ref state_node, const_tree type) +set_type_attr (diagnostics::digraphs::node &state_node, + const_tree type) { gcc_assert (type); pretty_printer pp; pp_format_decoder (&pp) = default_tree_printer; pp_printf (&pp, "%T", type); - state_node.set_type (pp_formatted_text (&pp)); + state_node.set_property (node_properties::type, + pp_formatted_text (&pp)); } static void -set_bits_attr (state_node_ref state_node, +set_bits_attr (diagnostics::digraphs::node & state_node, bit_range bits) { pretty_printer pp; bits.dump_to_pp (&pp); - state_node.set_attr ("bits", pp_formatted_text (&pp)); + state_node.set_property (node_properties::bits, + pp_formatted_text (&pp)); } +static void +set_value_attrs (diagnostics::digraphs::node &state_node, + const svalue &sval) +{ + state_node.set_property (node_properties::value, + sval.to_json ()); + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + sval.dump_to_pp (&pp, true); + state_node.set_property (node_properties::value_str, + pp_formatted_text (&pp)); +} + + // class analyzer_state_graph : public diagnostics::digraphs::digraph analyzer_state_graph::analyzer_state_graph (const program_state &state, @@ -141,34 +158,34 @@ analyzer_state_graph::analyzer_state_graph (const program_state &state, /* 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); + auto &dst_node = get_or_create_state_node (item.m_dst_reg); + add_edge (nullptr, item.m_src_node, dst_node); } } -state_node_ref +diagnostics::digraphs::node & analyzer_state_graph::get_or_create_state_node (const region ®) { auto existing = m_region_to_state_node_map.find (®); if (existing != m_region_to_state_node_map.end ()) return *existing->second; - auto ref = create_and_add_state_node (reg); - m_region_to_state_node_map[®] = &ref.m_node; - return ref; + auto &state_node = create_and_add_state_node (reg); + m_region_to_state_node_map[®] = &state_node; + return state_node; } -state_node_ref +diagnostics::digraphs::node & analyzer_state_graph::create_and_add_state_node (const region ®) { auto node = create_state_node (reg); - state_node_ref result = *node; + diagnostics::digraphs::node &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)); + auto &parent_state_node = get_or_create_state_node (*parent_reg); + parent_state_node.add_child (std::move (node)); return result; } add_node (std::move (node)); @@ -264,19 +281,18 @@ analyzer_state_graph::make_node_id (const region ®) std::unique_ptr analyzer_state_graph:: -make_state_node (diagnostics::state_graphs::node_kind kind, +make_state_node (enum node_properties::kind kind, std::string id) { auto node = std::make_unique (*this, std::move (id)); - state_node_ref node_ref (*node); - node_ref.set_node_kind (kind); + node->set_property (node_properties::kind, kind); return node; } std::unique_ptr analyzer_state_graph:: make_memspace_state_node (const region ®, - diagnostics::state_graphs::node_kind kind) + enum node_properties::kind kind) { return make_state_node (kind, make_node_id (reg)); } @@ -296,7 +312,7 @@ analyzer_state_graph::create_state_node (const region ®) const frame_region &frame_reg = static_cast (reg); - node = make_state_node (diagnostics::state_graphs::node_kind::stack_frame, + node = make_state_node (node_properties::kind::stack_frame, make_node_id (reg)); node->set_logical_loc (m_logical_loc_mgr.key_from_tree (frame_reg.get_fndecl ())); @@ -304,58 +320,59 @@ analyzer_state_graph::create_state_node (const region ®) pretty_printer pp; pp_format_decoder (&pp) = default_tree_printer; pp_printf (&pp, "%E", frame_reg.get_fndecl ()); - node->set_attr (STATE_NODE_PREFIX, "function", - pp_formatted_text (&pp)); + node->set_property (node_properties::function, + pp_formatted_text (&pp)); } } break; case RK_GLOBALS: node = make_memspace_state_node (reg, - diagnostics::state_graphs::node_kind::globals); + node_properties::kind::globals); break; case RK_CODE: node = make_memspace_state_node (reg, - diagnostics::state_graphs::node_kind::code); + node_properties::kind::code); break; case RK_FUNCTION: node = make_memspace_state_node (reg, - diagnostics::state_graphs::node_kind::function); + node_properties::kind::function); // TODO break; case RK_STACK: node = make_memspace_state_node (reg, - diagnostics::state_graphs::node_kind::stack); + node_properties::kind::stack); break; case RK_HEAP: node = make_memspace_state_node (reg, - diagnostics::state_graphs::node_kind::heap_); + node_properties::kind::heap_); break; case RK_THREAD_LOCAL: node = make_memspace_state_node (reg, - diagnostics::state_graphs::node_kind::thread_local_); + node_properties::kind::thread_local_); break; case RK_ROOT: gcc_unreachable (); break; case RK_SYMBOLIC: node = make_memspace_state_node (reg, - diagnostics::state_graphs::node_kind::other); + node_properties::kind::other); break; case RK_DECL: { - node = make_state_node (diagnostics::state_graphs::node_kind::variable, + node = make_state_node (node_properties::kind::variable, make_node_id (reg)); const decl_region &decl_reg = static_cast (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 ()); - node_ref.set_name (pp_formatted_text (&pp)); + node->set_property (node_properties::name, + pp_formatted_text (&pp)); } set_type_attr (*node, TREE_TYPE (decl_reg.get_decl ())); } @@ -377,14 +394,14 @@ analyzer_state_graph::create_state_node (const region ®) case RK_ERRNO: case RK_PRIVATE: case RK_UNKNOWN: - node = make_state_node (diagnostics::state_graphs::node_kind::other, + node = make_state_node (node_properties::kind::other, make_node_id (reg)); break; case RK_HEAP_ALLOCATED: case RK_ALLOCA: node = make_memspace_state_node (reg, - diagnostics::state_graphs::node_kind::dynalloc_buffer); + node_properties::kind::dynalloc_buffer); set_attr_for_dynamic_extents (reg, *node); break; } @@ -425,9 +442,9 @@ create_state_nodes_for_binding_cluster (const binding_cluster &cluster, get_or_create_state_node (*reg); } - auto ref = get_or_create_state_node (*cluster.get_base_region ()); + auto &ref = get_or_create_state_node (*cluster.get_base_region ()); - ref.m_node.add_child (create_state_node_for_conc_bindings (conc_bindings)); + ref.add_child (create_state_node_for_conc_bindings (conc_bindings)); const region *typed_reg = cluster.get_base_region (); if (!typed_reg->get_type ()) @@ -455,23 +472,18 @@ create_state_nodes_for_binding_cluster (const binding_cluster &cluster, std::unique_ptr analyzer_state_graph::create_state_node_for_conc_bindings (const concrete_bindings_t &conc_bindings) { - auto node = make_state_node (diagnostics::state_graphs::node_kind::other, + auto node = make_state_node (node_properties::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_state_node - = make_state_node (diagnostics::state_graphs::node_kind::other, + = make_state_node (node_properties::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_state_node->set_attr (STATE_NODE_PREFIX, "value", - pp_formatted_text (&pp)); - } + gcc_assert (sval); + set_value_attrs (*binding_state_node, *sval); node->add_child (std::move (binding_state_node)); } return node; @@ -496,27 +508,28 @@ analyzer_state_graph::get_bit_range_within_base_region (const region ®, void analyzer_state_graph:: -populate_state_node_for_typed_region (state_node_ref node, +populate_state_node_for_typed_region (diagnostics::digraphs::node &state_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 (node, reg_type); + set_type_attr (state_node, reg_type); bit_range bits (0, 0); if (get_bit_range_within_base_region (reg, bits)) { - set_bits_attr (node, bits); + set_bits_attr (state_node, bits); auto search = conc_bindings.find (bits); if (search != conc_bindings.end ()) { const svalue *bound_sval = search->second; - node.set_json_attr ("value", bound_sval->to_json ()); + gcc_assert (bound_sval); + set_value_attrs (state_node, *bound_sval); if (const region *dst_reg = bound_sval->maybe_get_region ()) - m_pending_edges.push_back ({node, *dst_reg}); + m_pending_edges.push_back ({state_node, *dst_reg}); } } @@ -555,9 +568,10 @@ populate_state_node_for_typed_region (state_node_ref node, { auto child_state_node = make_state_node - (diagnostics::state_graphs::node_kind::element, + (node_properties::kind::element, make_node_id (*child_reg)); - set_wi_attr (*child_state_node, "index", idx, UNSIGNED); + set_wi_attr (*child_state_node, + node_properties::index, idx, UNSIGNED); // Recurse: gcc_assert (element_type); @@ -565,7 +579,7 @@ populate_state_node_for_typed_region (state_node_ref node, *child_reg, conc_bindings, create_all); - node.m_node.add_child (std::move (child_state_node)); + state_node.add_child (std::move (child_state_node)); } } } @@ -587,11 +601,12 @@ populate_state_node_for_typed_region (state_node_ref node, { auto child_state_node = make_state_node - (diagnostics::state_graphs::node_kind::padding, + (node_properties::kind::padding, make_node_id (*child_reg)); - set_wi_attr (*child_state_node, "num_bits", + set_wi_attr (*child_state_node, + node_properties::num_bits, item.m_bit_range.m_size_in_bits, SIGNED); - node.m_node.add_child (std::move (child_state_node)); + state_node.add_child (std::move (child_state_node)); } } else @@ -600,27 +615,27 @@ populate_state_node_for_typed_region (state_node_ref node, = m_mgr.get_field_region (®, const_cast (item.m_field)); if (show_child_state_node_for_child_region_p (*child_reg, - conc_bindings, - create_all)) + conc_bindings, + create_all)) { auto child_state_node = make_state_node - (diagnostics::state_graphs::node_kind::field, + (node_properties::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_state_node->set_attr (STATE_NODE_PREFIX, "name", - pp_formatted_text (&pp)); + child_state_node->set_property (node_properties::name, + pp_formatted_text (&pp)); } // Recurse: 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)); + *child_reg, + conc_bindings, + create_all); + state_node.add_child (std::move (child_state_node)); } } } @@ -630,8 +645,9 @@ populate_state_node_for_typed_region (state_node_ref node, } void -analyzer_state_graph::set_attr_for_dynamic_extents (const region ®, - state_node_ref node_ref) +analyzer_state_graph:: +set_attr_for_dynamic_extents (const region ®, + diagnostics::digraphs::node &state_node) { const svalue *sval = m_state.m_region_model->get_dynamic_extents (®); if (sval) @@ -642,15 +658,16 @@ analyzer_state_graph::set_attr_for_dynamic_extents (const region ®, pp_wide_int (&pp, wi::to_wide (cst), UNSIGNED); else sval->dump_to_pp (&pp, true); - node_ref.set_attr ("dynamic-extents", pp_formatted_text (&pp)); + state_node.set_property (state_node_properties::dynamic_extents, + pp_formatted_text (&pp)); } } bool analyzer_state_graph:: show_child_state_node_for_child_region_p (const region ®, - const concrete_bindings_t &conc_bindings, - bool create_all) + const concrete_bindings_t &conc_bindings, + bool create_all) { if (create_all) return true; diff --git a/gcc/analyzer/ana-state-to-diagnostic-state.h b/gcc/analyzer/ana-state-to-diagnostic-state.h index 3a5ccc1b430..eec3d56b301 100644 --- a/gcc/analyzer/ana-state-to-diagnostic-state.h +++ b/gcc/analyzer/ana-state-to-diagnostic-state.h @@ -23,34 +23,38 @@ along with GCC; see the file COPYING3. If not see #include "diagnostics/state-graphs.h" #include "tree-logical-location.h" +#include "custom-sarif-properties/state-graphs.h" namespace ana { +namespace state_node_properties = custom_sarif_properties::state_graphs::node; + class analyzer_state_graph : public diagnostics::digraphs::digraph { public: analyzer_state_graph (const program_state &state, const extrinsic_state &ext_state); - diagnostics::state_graphs::state_node_ref + diagnostics::digraphs::node & get_or_create_state_node (const region ®); private: + struct pending_edge { - diagnostics::state_graphs::state_node_ref m_src_node; + diagnostics::digraphs::node & m_src_node; const region &m_dst_reg; }; - - diagnostics::state_graphs::state_node_ref + + diagnostics::digraphs::node & create_and_add_state_node (const region ®); std::unique_ptr - make_state_node (diagnostics::state_graphs::node_kind kind, + make_state_node (enum state_node_properties::kind kind, std::string id); std::unique_ptr make_memspace_state_node (const region ®, - enum diagnostics::state_graphs::node_kind kind); + enum state_node_properties::kind kind); std::unique_ptr create_state_node (const region ®); @@ -71,14 +75,14 @@ private: bit_range &out); void - populate_state_node_for_typed_region (diagnostics::state_graphs::state_node_ref, + populate_state_node_for_typed_region (diagnostics::digraphs::node &, const region ®, const concrete_bindings_t &conc_bindings, bool create_all); void set_attr_for_dynamic_extents (const region ®, - diagnostics::state_graphs::state_node_ref); + diagnostics::digraphs::node &); bool show_child_state_node_for_child_region_p (const region ®, @@ -95,7 +99,8 @@ private: const program_state &m_state; const extrinsic_state &m_ext_state; region_model_manager &m_mgr; - std::map m_region_to_state_node_map; + std::map m_region_to_state_node_map; std::map m_types_for_untyped_regions; unsigned m_next_id; std::vector m_pending_edges; diff --git a/gcc/analyzer/checker-event.cc b/gcc/analyzer/checker-event.cc index 4eac9450469..790ebc71438 100644 --- a/gcc/analyzer/checker-event.cc +++ b/gcc/analyzer/checker-event.cc @@ -29,6 +29,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-logical-location.h" #include "diagnostics/sarif-sink.h" #include "diagnostics/state-graphs.h" +#include "custom-sarif-properties/state-graphs.h" #include "analyzer/analyzer-logging.h" #include "analyzer/sm.h" @@ -242,9 +243,11 @@ checker_event::maybe_make_diagnostic_state_graph (bool debug) const pretty_printer pp; text_art::theme *theme = global_dc->get_diagram_theme (); text_art::dump_to_pp (*state, theme, &pp); - result->set_attr (STATE_GRAPH_PREFIX, - "analyzer/program_state/", - pp_formatted_text (&pp)); + const json::string_property program_state_property + (custom_sarif_properties::state_graphs::graph::prefix, + "analyzer/program_state/"); + result->set_property (program_state_property, + pp_formatted_text (&pp)); } return result; diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc index a6b14219068..b25e2adf015 100644 --- a/gcc/analyzer/sm-malloc.cc +++ b/gcc/analyzer/sm-malloc.cc @@ -2735,7 +2735,7 @@ 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 +static enum custom_sarif_properties::state_graphs::node::dynalloc_state get_dynalloc_state_for_state (enum resource_state rs) { switch (rs) @@ -2746,17 +2746,17 @@ get_dynalloc_state_for_state (enum resource_state rs) case RS_NULL: case RS_NON_HEAP: case RS_STOP: - return diagnostics::state_graphs::node_dynalloc_state::unknown; + return state_node_properties::dynalloc_state::unknown; case RS_ASSUMED_NON_NULL: - return diagnostics::state_graphs::node_dynalloc_state::nonnull; + return state_node_properties::dynalloc_state::nonnull; case RS_UNCHECKED: - return diagnostics::state_graphs::node_dynalloc_state::unchecked; + return state_node_properties::dynalloc_state::unchecked; case RS_NONNULL: - return diagnostics::state_graphs::node_dynalloc_state::nonnull; + return state_node_properties::dynalloc_state::nonnull; case RS_FREED: - return diagnostics::state_graphs::node_dynalloc_state::freed; + return state_node_properties::dynalloc_state::freed; } } @@ -2768,24 +2768,23 @@ add_state_to_state_graph (analyzer_state_graph &out_state_graph, { if (const region *reg = sval.maybe_get_region ()) { - auto reg_node = out_state_graph.get_or_create_state_node (*reg); + auto ®_node = out_state_graph.get_or_create_state_node (*reg); auto alloc_state = as_a_allocation_state (state); gcc_assert (alloc_state); - reg_node.set_dynalloc_state - (get_dynalloc_state_for_state (alloc_state->m_rs)); + reg_node.set_property (state_node_properties::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_node.m_node.set_attr (STATE_NODE_PREFIX, - "expected-deallocators", - pp_formatted_text (&pp)); + reg_node.set_property (state_node_properties::expected_deallocators, + pp_formatted_text (&pp)); } if (alloc_state->m_deallocator) - reg_node.m_node.set_attr (STATE_NODE_PREFIX, - "deallocator", - alloc_state->m_deallocator->m_name); + reg_node.set_property (state_node_properties::deallocator, + alloc_state->m_deallocator->m_name); } } diff --git a/gcc/configure b/gcc/configure index 38d8cd919cb..a742ad7e64c 100755 --- a/gcc/configure +++ b/gcc/configure @@ -36868,7 +36868,7 @@ $as_echo "$as_me: executing $ac_file commands" >&6;} "depdir":C) $SHELL $ac_aux_dir/mkinstalldirs $DEPDIR ;; "gccdepdir":C) ${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs build/$DEPDIR - for lang in $subdirs c-family common analyzer diagnostics text-art rtl-ssa sym-exec + for lang in $subdirs c-family common analyzer custom-sarif-properties diagnostics text-art rtl-ssa sym-exec do ${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs $lang/$DEPDIR done ;; diff --git a/gcc/configure.ac b/gcc/configure.ac index 19975fa5be5..253d3ff28e9 100644 --- a/gcc/configure.ac +++ b/gcc/configure.ac @@ -1368,7 +1368,7 @@ AC_CHECK_HEADERS(ext/hash_map) ZW_CREATE_DEPDIR AC_CONFIG_COMMANDS([gccdepdir],[ ${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs build/$DEPDIR - for lang in $subdirs c-family common analyzer diagnostics text-art rtl-ssa sym-exec + for lang in $subdirs c-family common analyzer custom-sarif-properties diagnostics text-art rtl-ssa sym-exec do ${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs $lang/$DEPDIR done], [subdirs="$subdirs" ac_aux_dir=$ac_aux_dir DEPDIR=$DEPDIR]) diff --git a/gcc/custom-sarif-properties/digraphs.cc b/gcc/custom-sarif-properties/digraphs.cc new file mode 100644 index 00000000000..30ca2b6cdd0 --- /dev/null +++ b/gcc/custom-sarif-properties/digraphs.cc @@ -0,0 +1,28 @@ +/* Extra properties for digraphs in SARIF property bags. + Copyright (C) 2025 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 +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "json.h" +#include "custom-sarif-properties/digraphs.h" + +const json::string_property custom_sarif_properties::digraphs::digraph::kind + ("gcc/digraphs/graph/kind"); diff --git a/gcc/custom-sarif-properties/digraphs.h b/gcc/custom-sarif-properties/digraphs.h new file mode 100644 index 00000000000..93817ed97bd --- /dev/null +++ b/gcc/custom-sarif-properties/digraphs.h @@ -0,0 +1,37 @@ +/* Extra properties for digraphs in SARIF property bags. + Copyright (C) 2025 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_CUSTOM_SARIF_PROPERTIES_DIGRAPHS_H +#define GCC_CUSTOM_SARIF_PROPERTIES_DIGRAPHS_H + +/* SARIF property names relating to digraphs. */ + +namespace custom_sarif_properties { + namespace digraphs { + namespace digraph { + /* A hint about the kind of graph we have, + and thus what kinds of nodes and edges to expect. */ + extern const json::string_property kind; + // string; values: "cfg" + } + } +} + +#endif /* ! GCC_CUSTOM_SARIF_PROPERTIES_DIGRAPHS_H */ diff --git a/gcc/custom-sarif-properties/state-graphs.cc b/gcc/custom-sarif-properties/state-graphs.cc new file mode 100644 index 00000000000..3e0e58a4216 --- /dev/null +++ b/gcc/custom-sarif-properties/state-graphs.cc @@ -0,0 +1,157 @@ +/* Properties for capturing state graphs in SARIF property bags. + Copyright (C) 2025 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 +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "json.h" +#include "custom-sarif-properties/state-graphs.h" + +/* graph. */ +namespace graph = custom_sarif_properties::state_graphs::graph; +#define STATE_GRAPH_PREFIX "gcc/diagnostic_state_graph/" +const char *const graph::prefix = STATE_GRAPH_PREFIX; +#undef STATE_GRAPH_PREFIX + +/* node. */ +namespace node = custom_sarif_properties::state_graphs::node; +#define STATE_NODE_PREFIX "gcc/diagnostic_state_node/" + +const json::enum_property node::kind + (STATE_NODE_PREFIX "kind"); + +const json::string_property node::function (STATE_NODE_PREFIX "function"); + +const json::string_property node::dynamic_extents + (STATE_NODE_PREFIX "dynamic-extents"); + +const json::string_property node::name (STATE_NODE_PREFIX "name"); +const json::string_property node::type (STATE_NODE_PREFIX "type"); +const json::json_property node::value (STATE_NODE_PREFIX "value"); +const json::string_property node::value_str (STATE_NODE_PREFIX "value_str"); + +const json::string_property node::index (STATE_NODE_PREFIX "index"); + +const json::string_property node::bits (STATE_NODE_PREFIX "bits"); + +const json::string_property node::num_bits (STATE_NODE_PREFIX "num_bits"); + +const json::string_property node::deallocator (STATE_NODE_PREFIX "deallocator"); + +const json::string_property node::expected_deallocators + (STATE_NODE_PREFIX "expected-deallocators"); + +const json::enum_property node::dynalloc_state + (STATE_NODE_PREFIX "dynalloc-state"); + +#undef STATE_NODE_PREFIX + + +/* edge. */ +namespace edge_props = custom_sarif_properties::state_graphs::edge; +#define STATE_EDGE_PREFIX "gcc/diagnostic_state_edge/" +extern const char *const edge_props::prefix = STATE_EDGE_PREFIX; +#undef STATE_EDGE_PREFIX + +// Traits for enum node:kind + +template<> +enum node::kind +json::enum_traits::get_unknown_value () +{ + return node::kind::other; +} + +static const char * const node_kind_strs[] = { + "globals", + "code", + "function", + "stack", + "stack-frame", + "heap", + "thread-local", + "dynalloc-buffer", + "variable", + "field", + "padding", + "element", + "other", +}; + +template<> +bool +json::enum_traits:: +maybe_get_value_from_string (const char *str, + enum_t &out) +{ + for (size_t i = 0; i < ARRAY_SIZE (node_kind_strs); ++i) + if (!strcmp (node_kind_strs[i], str)) + { + out = static_cast (i); + return true; + } + return false; +} + +template<> +const char * +json::enum_traits::get_string_for_value (enum_t value) +{ + return node_kind_strs[static_cast (value)]; +} + +// Traits for enum node:dynalloc_state + +template<> +enum node::dynalloc_state +json::enum_traits::get_unknown_value () +{ + return node::dynalloc_state::unknown; +} + +static const char * const dynalloc_state_strs[] = { + "unknown", + "nonnull", + "unchecked", + "freed" +}; + +template<> +bool +json::enum_traits:: +maybe_get_value_from_string (const char *str, + enum_t &out) +{ + for (size_t i = 0; i < ARRAY_SIZE (dynalloc_state_strs); ++i) + if (!strcmp (dynalloc_state_strs[i], str)) + { + out = static_cast (i); + return true; + } + return false; +} + +template<> +const char * +json::enum_traits:: +get_string_for_value (enum_t value) +{ + return dynalloc_state_strs[static_cast (value)]; +} diff --git a/gcc/custom-sarif-properties/state-graphs.h b/gcc/custom-sarif-properties/state-graphs.h new file mode 100644 index 00000000000..6ae9ad89135 --- /dev/null +++ b/gcc/custom-sarif-properties/state-graphs.h @@ -0,0 +1,97 @@ +/* Properties for capturing state graphs in SARIF property bags. + Copyright (C) 2025 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 +. */ + +#include "json.h" + +#ifndef GCC_DIAGNOSTICS_SARIF_PROPERTIES_STATE_GRAPHS_H +#define GCC_DIAGNOSTICS_SARIF_PROPERTIES_STATE_GRAPHS_H + +/* SARIF property names relating to GCC's CFGs. */ + +namespace custom_sarif_properties { + namespace state_graphs { + namespace graph { + extern const char *const prefix; + } + namespace node { + + enum class kind + { + // Memory regions + globals, + code, + function, // code within a particular function + stack, + stack_frame, + heap_, + thread_local_, + + /* Dynamically-allocated buffer, + on heap or stack (depending on parent). */ + dynalloc_buffer, + + variable, + + field, // field within a struct or union + padding, // padding bits in a struct or union + element, // element within an array + + other // anything else + }; + + enum class dynalloc_state + { + unknown, + nonnull, + unchecked, + freed + }; + + extern const json::enum_property kind; + + extern const json::string_property function; + extern const json::string_property dynamic_extents; + extern const json::string_property name; + extern const json::string_property type; + /* The value of a memory region, expressed as a json::value. */ + extern const json::json_property value; + /* The value of a memory region, expressed as a string. */ + extern const json::string_property value_str; + + /* For element nodes, the index within the array. */ + extern const json::string_property index; + + /* The range of bits or bytes within the base region. */ + extern const json::string_property bits; + + /* The size of a padding region. */ + extern const json::string_property num_bits; + + extern const json::string_property deallocator; + extern const json::string_property expected_deallocators; + extern const json::enum_property dynalloc_state; + } + namespace edge { + extern const char *const prefix; + } + } +} + +#endif /* ! GCC_DIAGNOSTICS_SARIF_PROPERTIES_STATE_GRAPHS_H */ diff --git a/gcc/diagnostics/diagnostics-selftests.cc b/gcc/diagnostics/diagnostics-selftests.cc index 94a212a6c93..757655bb172 100644 --- a/gcc/diagnostics/diagnostics-selftests.cc +++ b/gcc/diagnostics/diagnostics-selftests.cc @@ -46,7 +46,6 @@ run_diagnostics_selftests () sarif_sink_cc_tests (); digraphs_cc_tests (); output_spec_cc_tests (); - state_graphs_cc_tests (); lazy_paths_cc_tests (); paths_output_cc_tests (); changes_cc_tests (); diff --git a/gcc/diagnostics/diagnostics-selftests.h b/gcc/diagnostics/diagnostics-selftests.h index 994ebad5280..5a68a049d3e 100644 --- a/gcc/diagnostics/diagnostics-selftests.h +++ b/gcc/diagnostics/diagnostics-selftests.h @@ -44,7 +44,6 @@ extern void paths_output_cc_tests (); extern void sarif_sink_cc_tests (); extern void selftest_logical_locations_cc_tests (); extern void source_printing_cc_tests (); -extern void state_graphs_cc_tests (); } /* end of namespace diagnostics::selftest. */ diff --git a/gcc/diagnostics/digraphs.cc b/gcc/diagnostics/digraphs.cc index 4a2ea4fca3c..60d3e8ccfc7 100644 --- a/gcc/diagnostics/digraphs.cc +++ b/gcc/diagnostics/digraphs.cc @@ -30,13 +30,15 @@ along with GCC; see the file COPYING3. If not see #include "graphviz.h" #include "diagnostics/digraphs.h" #include "diagnostics/sarif-sink.h" +#include "custom-sarif-properties/digraphs.h" -#include "selftest.h" - +using digraph_object = diagnostics::digraphs::object; using digraph = diagnostics::digraphs::digraph; using digraph_node = diagnostics::digraphs::node; using digraph_edge = diagnostics::digraphs::edge; +namespace properties = custom_sarif_properties::digraphs; + namespace { class conversion_to_dot @@ -171,66 +173,145 @@ conversion_to_dot::has_edges_p (const digraph_node &input_node) // class object +/* String properties. */ + const char * -diagnostics::digraphs::object:: -get_attr (const char *key_prefix, const char *key) const +digraph_object::get_property (const json::string_property &property) const { if (!m_property_bag) return nullptr; - std::string prefixed_key = std::string (key_prefix) + key; - if (json::value *jv = m_property_bag->get (prefixed_key.c_str ())) + if (json::value *jv = m_property_bag->get (property.m_key.get ())) if (json::string *jstr = jv->dyn_cast_string ()) return jstr->get_string (); return nullptr; } void -diagnostics::digraphs::object:: -set_attr (const char *key_prefix, const char *key, const char *value) +digraph_object::set_property (const json::string_property &property, + const char *utf8_value) +{ + auto &bag = ensure_property_bag (); + bag.set_string (property.m_key.get (), utf8_value); +} + +/* Integer properties. */ + +bool +digraph_object::maybe_get_property (const json::integer_property &property, + long &out_value) const +{ + if (!m_property_bag) + return false; + if (json::value *jv = m_property_bag->get (property.m_key.get ())) + if (json::integer_number *jnum = jv->dyn_cast_integer_number ()) + { + out_value = jnum->get (); + return true; + } + return false; +} + +void +digraph_object::set_property (const json::integer_property &property, long value) +{ + auto &bag = ensure_property_bag (); + bag.set_integer (property.m_key.get (), value); +} + +/* Bool properties. */ +void +digraph_object::set_property (const json::bool_property &property, bool value) +{ + auto &bag = ensure_property_bag (); + bag.set_bool (property.m_key.get (), value); +} + +tristate +digraph_object:: +get_property_as_tristate (const json::bool_property &property) const +{ + if (m_property_bag) + { + if (json::value *jv = m_property_bag->get (property.m_key.get ())) + switch (jv->get_kind ()) + { + default: + break; + case json::JSON_TRUE: + return tristate (true); + case json::JSON_FALSE: + return tristate (false); + } + } + return tristate::unknown (); +} + +/* Array-of-string properties. */ +json::array * +digraph_object::get_property (const json::array_of_string_property &property) const { - set_json_attr (key_prefix, key, std::make_unique (value)); + if (m_property_bag) + if (json::value *jv = m_property_bag->get (property.m_key.get ())) + if (json::array *arr = jv->dyn_cast_array ()) + return arr; + return nullptr; +} + +/* json::value properties. */ +const json::value * +digraph_object::get_property (const json::json_property &property) const +{ + if (m_property_bag) + return m_property_bag->get (property.m_key.get ()); + return nullptr; } void -diagnostics::digraphs::object:: -set_json_attr (const char *key_prefix, const char *key, std::unique_ptr value) +digraph_object::set_property (const json::json_property &property, + std::unique_ptr value) +{ + auto &bag = ensure_property_bag (); + bag.set (property.m_key.get (), std::move (value)); +} + +json::object & +digraph_object::ensure_property_bag () { - std::string prefixed_key = std::string (key_prefix) + key; if (!m_property_bag) - m_property_bag = std::make_unique (); - m_property_bag->set (prefixed_key.c_str (), std::move (value)); + m_property_bag = std::make_unique ( ); + return *m_property_bag; } // class digraph DEBUG_FUNCTION void -diagnostics::digraphs::digraph::dump () const +digraph::dump () const { make_json_sarif_graph ()->dump (); } std::unique_ptr -diagnostics::digraphs::digraph::make_json_sarif_graph () const +digraph::make_json_sarif_graph () const { return make_sarif_graph (*this, nullptr, nullptr); } std::unique_ptr -diagnostics::digraphs::digraph::make_dot_graph () const +digraph::make_dot_graph () const { - conversion_to_dot to_dot; - return to_dot.make_dot_graph_from_diagnostic_graph (*this); + conversion_to_dot converter; + return converter.make_dot_graph_from_diagnostic_graph (*this); } -std::unique_ptr -diagnostics::digraphs::digraph::clone () const +std::unique_ptr +digraph::clone () const { auto result = std::make_unique (); if (get_property_bag ()) result->set_property_bag (get_property_bag ()->clone_as_object ()); - std::map node_mapping; + std::map node_mapping; for (auto &iter : m_nodes) result->add_node (iter->clone (*result, node_mapping)); @@ -241,10 +322,10 @@ diagnostics::digraphs::digraph::clone () const } void -diagnostics::digraphs::digraph::add_edge (const char *id, - node &src_node, - node &dst_node, - const char *label) +digraph::add_edge (const char *id, + node &src_node, + node &dst_node, + const char *label) { auto e = std::make_unique (*this, id, @@ -263,7 +344,7 @@ diagnostics::digraphs::digraph::add_edge (const char *id, to edges by id (SARIF 2.1.0's §3.43.2 edgeId property). */ std::string -diagnostics::digraphs::digraph::make_edge_id (const char *edge_id) +digraph::make_edge_id (const char *edge_id) { /* If we have an id, use it. */ if (edge_id) @@ -284,27 +365,38 @@ diagnostics::digraphs::digraph::make_edge_id (const char *edge_id) } } +const char * +digraph::get_graph_kind () const +{ + return get_property (properties::digraph::kind); +} + +void +digraph::set_graph_kind (const char *kind) +{ + set_property (properties::digraph::kind, kind); +} + // class node DEBUG_FUNCTION void -diagnostics::digraphs::node::dump () const +digraph_node::dump () const { to_json_sarif_node ()->dump (); } std::unique_ptr -diagnostics::digraphs::node::to_json_sarif_node () const +digraph_node::to_json_sarif_node () const { return make_sarif_node (*this, nullptr, nullptr); } -std::unique_ptr -diagnostics::digraphs::node::clone (digraph &new_graph, - std::map &node_mapping) const +std::unique_ptr +digraph_node::clone (digraph &new_graph, + std::map &node_mapping) const { auto result - = std::make_unique (new_graph, - get_id ()); + = std::make_unique (new_graph, get_id ()); node_mapping.insert ({const_cast (this), result.get ()}); result->set_logical_loc (m_logical_loc); @@ -353,6 +445,9 @@ diagnostics::digraphs::edge::to_json_sarif_edge () const #if CHECKING_P +#include "selftest.h" +#include "custom-sarif-properties/state-graphs.h" + namespace diagnostics { namespace selftest { @@ -391,16 +486,17 @@ test_simple_graph () #define KEY_PREFIX "/placeholder/" auto g = std::make_unique (); g->set_description ("test graph"); - g->set_attr (KEY_PREFIX, "date", "1066"); + g->set_property (json::string_property (KEY_PREFIX, "date"), "1066"); auto a = std::make_unique (*g, "a"); auto b = std::make_unique (*g, "b"); - b->set_attr (KEY_PREFIX, "color", "red"); + b->set_property (json::string_property (KEY_PREFIX, "color"), "red"); auto c = std::make_unique (*g, "c"); c->set_label ("I am a node label"); auto e = std::make_unique (*g, nullptr, *a, *c); - e->set_attr (KEY_PREFIX, "status", "copacetic"); + e->set_property (json::string_property (KEY_PREFIX, "status"), + "copacetic"); e->set_label ("I am an edge label"); g->add_edge (std::move (e)); @@ -449,6 +545,34 @@ test_simple_graph () } } +static void +test_property_objects () +{ + namespace state_node_properties = custom_sarif_properties::state_graphs::node; + + digraph g; + digraph_node node (g, "a"); + + ASSERT_EQ (node.get_property (state_node_properties::kind), + state_node_properties::kind::other); + node.set_property (state_node_properties::kind, + state_node_properties::kind::stack); + ASSERT_EQ (node.get_property (state_node_properties::kind), + state_node_properties::kind::stack); + + ASSERT_EQ (node.get_property (state_node_properties::dynalloc_state), + state_node_properties::dynalloc_state::unknown); + node.set_property (state_node_properties::dynalloc_state, + state_node_properties::dynalloc_state::freed); + ASSERT_EQ (node.get_property (state_node_properties::dynalloc_state), + state_node_properties::dynalloc_state::freed); + + ASSERT_EQ (node.get_property (state_node_properties::type), nullptr); + node.set_property (state_node_properties::type, "const char *"); + ASSERT_STREQ (node.get_property (state_node_properties::type), + "const char *"); +} + /* Run all of the selftests within this file. */ void @@ -456,6 +580,7 @@ digraphs_cc_tests () { test_empty_graph (); test_simple_graph (); + test_property_objects (); } } // namespace diagnostics::selftest diff --git a/gcc/diagnostics/digraphs.h b/gcc/diagnostics/digraphs.h index 7193ee48c3f..485a18917ca 100644 --- a/gcc/diagnostics/digraphs.h +++ b/gcc/diagnostics/digraphs.h @@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see #define GCC_DIAGNOSTICS_DIGRAPHS_H #include "json.h" +#include "tristate.h" #include "diagnostics/logical-locations.h" class graphviz_out; @@ -55,23 +56,57 @@ class edge; class object { public: - const char * - get_attr (const char *key_prefix, - const char *key) const; - + /* String properties. */ + const char *get_property (const json::string_property &property) const; + void set_property (const json::string_property &property, + const char *utf8_value); + + /* Integer properties. */ + bool maybe_get_property (const json::integer_property &property, long &out) const; + void set_property (const json::integer_property &property, long value); + + /* Bool properties. */ + tristate + get_property_as_tristate (const json::bool_property &property) const; + void set_property (const json::bool_property &property, bool value); + + /* Array-of-string properties. */ + json::array * + get_property (const json::array_of_string_property &property) const; + + /* enum properties. */ + template + EnumType + get_property (const json::enum_property &property) const + { + if (m_property_bag) + { + EnumType result; + if (m_property_bag->maybe_get_enum (property, result)) + return result; + } + return json::enum_traits::get_unknown_value (); + } + template void - set_attr (const char *key_prefix, - const char *key, - const char *value); + set_property (const json::enum_property &property, + EnumType value) + { + auto &bag = ensure_property_bag (); + bag.set_enum (property, value); + } - void - set_json_attr (const char *key_prefix, - const char *key, - std::unique_ptr value); + /* json::value properties. */ + const json::value *get_property (const json::json_property &property) const; + void set_property (const json::json_property &property, + std::unique_ptr value); json::object * get_property_bag () const { return m_property_bag.get (); } + json::object & + ensure_property_bag (); + void set_property_bag (std::unique_ptr property_bag) { @@ -188,6 +223,9 @@ class digraph : public object std::unique_ptr clone () const; + const char *get_graph_kind () const; + void set_graph_kind (const char *); + private: void add_node_id (std::string node_id, node &new_node) @@ -300,7 +338,7 @@ class node : public object clone (digraph &new_graph, std::map &node_mapping) const; - private: +private: std::string m_id; std::unique_ptr m_label; std::vector> m_children; diff --git a/gcc/diagnostics/html-sink.cc b/gcc/diagnostics/html-sink.cc index d3fb107e614..99d3b9d5dab 100644 --- a/gcc/diagnostics/html-sink.cc +++ b/gcc/diagnostics/html-sink.cc @@ -57,8 +57,8 @@ html_generation_options::html_generation_options () : m_css (true), m_javascript (true), m_show_state_diagrams (false), - m_show_state_diagrams_sarif (false), - m_show_state_diagrams_dot_src (false) + m_show_graph_sarif (false), + m_show_graph_dot_src (false) { } @@ -68,8 +68,8 @@ html_generation_options::dump (FILE *outfile, int indent) const DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_css); DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_javascript); DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_show_state_diagrams); - DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_show_state_diagrams_sarif); - DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_show_state_diagrams_dot_src); + DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_show_graph_sarif); + DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_show_graph_dot_src); } class html_builder; @@ -640,7 +640,7 @@ html_builder::maybe_make_state_diagram (const paths::event &event) the debug version. */ auto state_graph = event.maybe_make_diagnostic_state_graph - (m_html_gen_opts.m_show_state_diagrams_sarif); + (m_html_gen_opts.m_show_graph_sarif); if (!state_graph) return nullptr; @@ -652,7 +652,7 @@ html_builder::maybe_make_state_diagram (const paths::event &event) auto wrapper = std::make_unique ("div", false); xml::printer xp (*wrapper); - if (m_html_gen_opts.m_show_state_diagrams_sarif) + if (m_html_gen_opts.m_show_graph_sarif) { // For debugging, show the SARIF src inline: pretty_printer pp; @@ -660,7 +660,7 @@ html_builder::maybe_make_state_diagram (const paths::event &event) print_pre_source (xp, pp_formatted_text (&pp)); } - if (m_html_gen_opts.m_show_state_diagrams_dot_src) + if (m_html_gen_opts.m_show_graph_dot_src) { // For debugging, show the dot src inline: pretty_printer pp; @@ -1278,21 +1278,41 @@ void html_builder::add_graph (const digraphs::digraph &dg, xml::element &parent_element) { + auto div = std::make_unique ("div", false); + div->set_attr ("class", "gcc-directed-graph"); + xml::printer xp (*div); + + if (m_html_gen_opts.m_show_graph_sarif) + { + // For debugging, show the SARIF src inline: + pretty_printer pp; + dg.make_json_sarif_graph ()->print (&pp, true); + print_pre_source (xp, pp_formatted_text (&pp)); + } + if (auto dot_graph = dg.make_dot_graph ()) - if (auto svg_element = dot::make_svg_from_graph (*dot_graph)) - { - auto div = std::make_unique ("div", false); - div->set_attr ("class", "gcc-directed-graph"); - xml::printer xp (*div); - if (const char *description = dg.get_description ()) - { - xp.push_tag ("h2", true); - xp.add_text (description); - xp.pop_tag ("h2"); - } - xp.append (std::move (svg_element)); - parent_element.add_child (std::move (div)); - } + { + if (m_html_gen_opts.m_show_graph_dot_src) + { + // For debugging, show the dot src inline: + pretty_printer pp; + dot::writer w (pp); + dot_graph->print (w); + print_pre_source (xp, pp_formatted_text (&pp)); + } + + if (auto svg_element = dot::make_svg_from_graph (*dot_graph)) + { + if (const char *description = dg.get_description ()) + { + xp.push_tag ("h2", true); + xp.add_text (description); + xp.pop_tag ("h2"); + } + xp.append (std::move (svg_element)); + parent_element.add_child (std::move (div)); + } + } } void diff --git a/gcc/diagnostics/html-sink.h b/gcc/diagnostics/html-sink.h index d25ceea4b83..ad68e6fa3a6 100644 --- a/gcc/diagnostics/html-sink.h +++ b/gcc/diagnostics/html-sink.h @@ -40,11 +40,12 @@ struct html_generation_options // If true, attempt to show state diagrams at events bool m_show_state_diagrams; - // If true, show the SARIF form of the state with such diagrams - bool m_show_state_diagrams_sarif; + /* If true, show the SARIF form of the state with such diagrams, + and of other graphs. */ + bool m_show_graph_sarif; - // If true, show the .dot source used for the diagram - bool m_show_state_diagrams_dot_src; + // If true, show the .dot source used for such graphs + bool m_show_graph_dot_src; }; extern diagnostics::output_file diff --git a/gcc/diagnostics/output-spec.cc b/gcc/diagnostics/output-spec.cc index dfde7f0bdf3..f7cce0acce8 100644 --- a/gcc/diagnostics/output-spec.cc +++ b/gcc/diagnostics/output-spec.cc @@ -650,12 +650,12 @@ html_scheme_handler::maybe_handle_kv (const context &ctxt, if (key == "show-state-diagrams") return parse_bool_value (ctxt, key, value, m_html_gen_opts.m_show_state_diagrams); - if (key == "show-state-diagrams-dot-src") + if (key == "show-graph-dot-src") return parse_bool_value (ctxt, key, value, - m_html_gen_opts.m_show_state_diagrams_dot_src); - if (key == "show-state-diagrams-sarif") + m_html_gen_opts.m_show_graph_dot_src); + if (key == "show-graph-sarif") return parse_bool_value (ctxt, key, value, - m_html_gen_opts.m_show_state_diagrams_sarif); + m_html_gen_opts.m_show_graph_sarif); return result::unrecognized; } @@ -666,8 +666,8 @@ html_scheme_handler::get_keys (auto_vec &out) const out.safe_push ("file"); out.safe_push ("javascript"); out.safe_push ("show-state-diagrams"); - out.safe_push ("show-state-diagrams-dot-src"); - out.safe_push ("show-state-diagrams-sarif"); + out.safe_push ("show-graph-dot-src"); + out.safe_push ("show-graph-sarif"); } } // namespace output_spec diff --git a/gcc/diagnostics/state-graphs-to-dot.cc b/gcc/diagnostics/state-graphs-to-dot.cc index 2d80e6b283f..8a3ad246d1a 100644 --- a/gcc/diagnostics/state-graphs-to-dot.cc +++ b/gcc/diagnostics/state-graphs-to-dot.cc @@ -27,6 +27,7 @@ along with GCC; see the file COPYING3. If not see #include "system.h" #include "coretypes.h" +#include "custom-sarif-properties/state-graphs.h" #include "diagnostics/state-graphs.h" #include "graphviz.h" #include "xml.h" @@ -36,6 +37,8 @@ along with GCC; see the file COPYING3. If not see using namespace diagnostics; using namespace diagnostics::state_graphs; +namespace state_node_properties = custom_sarif_properties::state_graphs::node; + static int get_depth (const digraphs::node &n) { @@ -47,28 +50,28 @@ get_depth (const digraphs::node &n) } static const char * -get_color_for_dynalloc_state (enum node_dynalloc_state dynalloc_st) +get_color_for_dynalloc_state (enum state_node_properties::dynalloc_state dynalloc_st) { switch (dynalloc_st) { default: gcc_unreachable (); break; - case node_dynalloc_state::unknown: - case node_dynalloc_state::nonnull: + case state_node_properties::dynalloc_state::unknown: + case state_node_properties::dynalloc_state::nonnull: return nullptr; - case node_dynalloc_state::unchecked: + case state_node_properties::dynalloc_state::unchecked: return "#ec7a08"; // pf-orange-400 - case node_dynalloc_state::freed: + case state_node_properties::dynalloc_state::freed: return "#cc0000"; // pf-red-100 } } static void set_color_for_dynalloc_state (dot::attr_list &attrs, - enum node_dynalloc_state state) + enum state_node_properties::dynalloc_state state) { if (const char *color = get_color_for_dynalloc_state (state)) attrs.add (dot::id ("color"), dot::id (color)); @@ -106,7 +109,7 @@ public: = std::make_unique (dot::id ("cluster_memory_regions")); for (size_t i = 0; i < input_state_graph.get_num_nodes (); ++i) on_input_state_node (*root_cluster, - state_node_ref (input_state_graph.get_node (i))); + input_state_graph.get_node (i)); add_stmt (std::move (root_cluster)); /* Now create dot edges for edges in input_stage_graph. */ @@ -126,7 +129,8 @@ public: auto e = std::make_unique (src_port_id->second, dst_port_id->second); set_color_for_dynalloc_state - (e->m_attrs, state_node_ref (dst_node).get_dynalloc_state ()); + (e->m_attrs, + dst_node.get_property (state_node_properties::dynalloc_state)); add_stmt (std::move (e)); } @@ -147,9 +151,9 @@ private: } dot::id - make_id (state_node_ref state_node, bool cluster) + make_id (const diagnostics::digraphs::node &state_node, bool cluster) { - std::string input_node_id = state_node.m_node.get_id (); + std::string input_node_id = state_node.get_id (); if (cluster) return std::string ("cluster_") + input_node_id; else @@ -157,44 +161,44 @@ private: } bool - starts_node_p (state_node_ref state_node) + starts_node_p (const diagnostics::digraphs::node &state_node) { - switch (state_node.get_node_kind ()) + switch (state_node.get_property (state_node_properties::kind)) { default: return false; - case node_kind::stack: + case state_node_properties::kind::stack: /* We want all frames in the stack in the same table, so they are grouped. */ - case node_kind::dynalloc_buffer: - case node_kind::variable: + case state_node_properties::kind::dynalloc_buffer: + case state_node_properties::kind::variable: return true; } } const char * - get_label_for_node (state_node_ref state_node) + get_label_for_node (const diagnostics::digraphs::node &state_node) { - switch (state_node.get_node_kind ()) + switch (state_node.get_property (state_node_properties::kind)) { default: return nullptr; - case node_kind::globals: + case state_node_properties::kind::globals: return _("Globals"); - case node_kind::code: + case state_node_properties::kind::code: return _("Code"); - case node_kind::stack: + case state_node_properties::kind::stack: return _("Stack"); - case node_kind::heap_: + case state_node_properties::kind::heap_: return _("Heap"); } } void on_input_state_node (dot::subgraph &parent_subgraph, - state_node_ref state_node) + const diagnostics::digraphs::node &state_node) { dot::id sg_id = make_id (state_node, true); @@ -207,7 +211,7 @@ private: xp.set_attr ("cellborder", "1"); xp.set_attr ("cellspacing", "0"); - const int max_depth = get_depth (state_node.m_node); + const int max_depth = get_depth (state_node); const int num_columns = max_depth + 2; dot::id id_of_dot_node = make_id (state_node, false); @@ -233,9 +237,9 @@ private: child_subgraph->add_attr (dot::id ("label"), dot::id (label)); // recurse: - for (size_t i = 0; i < state_node.m_node.get_num_children (); ++i) + for (size_t i = 0; i < state_node.get_num_children (); ++i) on_input_state_node (*child_subgraph, - state_node.m_node.get_child (i)); + state_node.get_child (i)); parent_subgraph.m_stmt_list.add_stmt (std::move (child_subgraph)); } } @@ -246,10 +250,10 @@ private: add_title_tr (const dot::id &id_of_dot_node, xml::printer &xp, int num_columns, - state_node_ref state_node, + const diagnostics::digraphs::node &state_node, std::string heading, enum style styl, - enum node_dynalloc_state dynalloc_state) + enum state_node_properties::dynalloc_state dynalloc_state) { xp.push_tag ("tr", true); xp.push_tag ("td", false); @@ -298,48 +302,49 @@ private: void on_node_in_table (const dot::id &id_of_dot_node, xml::printer &xp, - state_node_ref state_node, + const diagnostics::digraphs::node &state_node, int max_depth, int depth, int num_columns) { bool recurse = true; - auto input_node_kind = state_node.get_node_kind (); + auto input_node_kind = state_node.get_property (state_node_properties::kind); switch (input_node_kind) { - case node_kind::padding: - case node_kind::other: + case state_node_properties::kind::padding: + case state_node_properties::kind::other: return; - case node_kind::stack: + case state_node_properties::kind::stack: add_title_tr (id_of_dot_node, xp, num_columns, state_node, "Stack", style::h1, - node_dynalloc_state::unknown); + state_node_properties::dynalloc_state::unknown); break; - case node_kind::stack_frame: + case state_node_properties::kind::stack_frame: if (auto logical_loc = state_node.get_logical_loc ()) if (const char *function = m_logical_loc_mgr.get_short_name (logical_loc)) add_title_tr (id_of_dot_node, xp, num_columns, state_node, std::string ("Frame: ") + function, style::h2, - node_dynalloc_state::unknown); + state_node_properties::dynalloc_state::unknown); break; - case node_kind::dynalloc_buffer: + case state_node_properties::kind::dynalloc_buffer: { - enum node_dynalloc_state dynalloc_st - = state_node.get_dynalloc_state (); - const char *extents = state_node.get_dynamic_extents (); - const char *type = state_node.get_type (); + enum state_node_properties::dynalloc_state dynalloc_st + = state_node.get_property (state_node_properties::dynalloc_state); + const char *extents + = state_node.get_property (state_node_properties::dynamic_extents); + const char *type = state_node.get_property (state_node_properties::type); pretty_printer pp; switch (dynalloc_st) { default: gcc_unreachable (); - case node_dynalloc_state::unknown: - case node_dynalloc_state::nonnull: + case state_node_properties::dynalloc_state::unknown: + case state_node_properties::dynalloc_state::nonnull: if (type) { if (extents) @@ -356,7 +361,7 @@ private: } break; - case node_dynalloc_state::unchecked: + case state_node_properties::dynalloc_state::unchecked: if (type) { if (extents) @@ -371,7 +376,7 @@ private: } break; - case node_dynalloc_state::freed: + case state_node_properties::dynalloc_state::freed: // TODO: show deallocator // TODO: show deallocation event pp_printf (&pp, "Freed buffer"); @@ -404,9 +409,10 @@ private: { default: break; - case node_kind::variable: + case state_node_properties::kind::variable: { - const char *name = state_node.get_name (); + const char *name + = state_node.get_property (state_node_properties::name); gcc_assert (name); xp.push_tag ("td", false); maybe_add_dst_port (id_of_dot_node, xp, state_node); @@ -416,9 +422,10 @@ private: xp.pop_tag ("td"); } break; - case node_kind::element: + case state_node_properties::kind::element: { - const char *index = state_node.get_index (); + const char *index + = state_node.get_property (state_node_properties::index); gcc_assert (index); xp.push_tag ("td", false); maybe_add_dst_port (id_of_dot_node, xp, state_node); @@ -430,9 +437,10 @@ private: xp.pop_tag ("td"); } break; - case node_kind::field: + case state_node_properties::kind::field: { - const char *name = state_node.get_name (); + const char *name + = state_node.get_property (state_node_properties::name); gcc_assert (name); xp.push_tag ("td", false); maybe_add_dst_port (id_of_dot_node, xp, state_node); @@ -445,7 +453,8 @@ private: break; } - if (const char *type = state_node.get_type ()) + if (const char *type + = state_node.get_property (state_node_properties::type)) { xp.push_tag ("td", false); xp.set_attr ("align", "right"); @@ -455,7 +464,8 @@ private: xp.pop_tag ("td"); } - if (const char *value = state_node.get_value ()) + if (const char *value + = state_node.get_property (state_node_properties::value_str)) { xp.push_tag ("td", false); xp.set_attr ("align", "left"); @@ -466,15 +476,16 @@ private: xp.pop_tag ("td"); recurse = false; } + xp.pop_tag ("tr"); } break; } if (recurse) - for (size_t i = 0; i < state_node.m_node.get_num_children (); ++i) + for (size_t i = 0; i < state_node.get_num_children (); ++i) on_node_in_table (id_of_dot_node, xp, - state_node.m_node.get_child (i), + state_node.get_child (i), max_depth, depth + 1, num_columns); } @@ -497,9 +508,9 @@ private: void maybe_add_src_port (const dot::id &id_of_dot_node, xml::printer &xp, - state_node_ref state_node) + const diagnostics::digraphs::node &state_node) { - auto iter = m_src_nodes.find (&state_node.m_node); + auto iter = m_src_nodes.find (&state_node); if (iter == m_src_nodes.end ()) return; @@ -507,7 +518,7 @@ private: dot::node_id node_id (id_of_dot_node, dot::port (src_id, dot::compass_pt::e)); - m_src_node_to_port_id.insert ({&state_node.m_node, node_id}); + m_src_node_to_port_id.insert ({&state_node, node_id}); xp.set_attr ("port", src_id.m_str); } @@ -517,9 +528,9 @@ private: void maybe_add_dst_port (const dot::id &id_of_dot_node, xml::printer &xp, - state_node_ref state_node) + const diagnostics::digraphs::node &state_node) { - auto iter = m_dst_nodes.find (&state_node.m_node); + auto iter = m_dst_nodes.find (&state_node); if (iter == m_dst_nodes.end ()) return; @@ -527,7 +538,7 @@ private: dot::node_id node_id (id_of_dot_node, dot::port (dst_id/*, dot::compass_pt::w*/)); - m_dst_node_to_port_id.insert ({&state_node.m_node, node_id}); + m_dst_node_to_port_id.insert ({&state_node, node_id}); xp.set_attr ("port", dst_id.m_str); } @@ -535,11 +546,11 @@ private: const logical_locations::manager &m_logical_loc_mgr; /* All nodes involved in edges (and thus will need a port). */ - std::set m_src_nodes; - std::set m_dst_nodes; + std::set m_src_nodes; + std::set m_dst_nodes; - std::map m_src_node_to_port_id; - std::map m_dst_node_to_port_id; + std::map m_src_node_to_port_id; + std::map m_dst_node_to_port_id; }; std::unique_ptr diff --git a/gcc/diagnostics/state-graphs.cc b/gcc/diagnostics/state-graphs.cc deleted file mode 100644 index 5941c413821..00000000000 --- a/gcc/diagnostics/state-graphs.cc +++ /dev/null @@ -1,156 +0,0 @@ -/* Extensions to diagnostics::digraphs to support state graphs. - Copyright (C) 2025 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 -. */ - -#define INCLUDE_ALGORITHM -#define INCLUDE_MAP -#define INCLUDE_SET -#define INCLUDE_STRING -#define INCLUDE_VECTOR -#include "config.h" -#include "system.h" -#include "coretypes.h" - -#include "diagnostics/state-graphs.h" -#include "selftest.h" - -using namespace diagnostics::state_graphs; - -const char * const node_kind_strs[] = { - "globals", - "code", - "function", - "stack", - "stack-frame", - "heap", - "thread-local", - "dynalloc-buffer", - "variable", - "field", - "padding", - "element", - "other", -}; - -const char * -diagnostics::state_graphs::node_kind_to_str (enum node_kind k) -{ - return node_kind_strs[static_cast (k)]; -} - -// struct state_node_ref - -enum node_kind -state_node_ref::get_node_kind () const -{ - const char *value = get_attr ("kind"); - if (!value) - return node_kind::other; - - for (size_t i = 0; i < ARRAY_SIZE (node_kind_strs); ++i) - if (!strcmp (node_kind_strs[i], value)) - return static_cast (i); - - return node_kind::other; -} - -void -state_node_ref::set_node_kind (enum node_kind k) -{ - set_attr ("kind", node_kind_to_str (k)); -} - -const char * const dynalloc_state_strs[] = { - "unknown", - "nonnull", - "unchecked", - "freed" -}; - -enum node_dynalloc_state -state_node_ref::get_dynalloc_state () const -{ - const char *value = get_attr ("dynalloc-state"); - if (!value) - return node_dynalloc_state::unknown; - - for (size_t i = 0; i < ARRAY_SIZE (dynalloc_state_strs); ++i) - if (!strcmp (dynalloc_state_strs[i], value)) - return static_cast (i); - - return node_dynalloc_state::unknown; -} - -void -state_node_ref::set_dynalloc_state (enum node_dynalloc_state s) const -{ - set_attr ("dynalloc-state", - dynalloc_state_strs[static_cast (s)]); -} - -const char * -state_node_ref::get_dynamic_extents () const -{ - return m_node.get_attr (STATE_NODE_PREFIX, "dynamic-extents"); -} - -void -state_node_ref::set_json_attr (const char *key, - std::unique_ptr value) const -{ - m_node.set_json_attr (STATE_NODE_PREFIX, key, std::move (value)); -} - -#if CHECKING_P - -namespace diagnostics { -namespace selftest { - -static void -test_node_attrs () -{ - digraphs::digraph g; - digraphs::node n (g, "a"); - state_node_ref node_ref (n); - - ASSERT_EQ (node_ref.get_node_kind (), node_kind::other); - node_ref.set_node_kind (node_kind::stack); - ASSERT_EQ (node_ref.get_node_kind (), node_kind::stack); - - ASSERT_EQ (node_ref.get_dynalloc_state (), node_dynalloc_state::unknown); - node_ref.set_dynalloc_state (node_dynalloc_state::freed); - ASSERT_EQ (node_ref.get_dynalloc_state (), node_dynalloc_state::freed); - - ASSERT_EQ (node_ref.get_type (), nullptr); - node_ref.set_type ("const char *"); - ASSERT_STREQ (node_ref.get_type (), "const char *"); -} - -/* Run all of the selftests within this file. */ - -void -state_graphs_cc_tests () -{ - test_node_attrs (); -} - -} // namespace diagnostics::selftest -} // namespace diagnostics - -#endif /* CHECKING_P */ diff --git a/gcc/diagnostics/state-graphs.h b/gcc/diagnostics/state-graphs.h index ad18f82b5b7..21aded03ff6 100644 --- a/gcc/diagnostics/state-graphs.h +++ b/gcc/diagnostics/state-graphs.h @@ -22,7 +22,6 @@ along with GCC; see the file COPYING3. If not see #define GCC_DIAGNOSTICS_STATE_GRAPHS_H #include "diagnostics/digraphs.h" -#include "diagnostics/logical-locations.h" /* diagnostics::digraphs provides support for directed graphs. @@ -34,118 +33,11 @@ along with GCC; see the file COPYING3. If not see in these nodes to stash extra properties (e.g. what kind of memory region a node is e.g. stack vs heap). */ -class sarif_graph; namespace dot { class graph; } namespace diagnostics { namespace state_graphs { -enum class node_kind -{ - // Memory regions - globals, - code, - function, // code within a particular function - stack, - stack_frame, - heap_, - thread_local_, - - /* Dynamically-allocated buffer, - on heap or stack (depending on parent). */ - dynalloc_buffer, - - variable, - - field, // field within a struct or union - padding, // padding bits in a struct or union - element, // element within an array - - other // anything else -}; - -extern const char * -node_kind_to_str (enum node_kind); - -enum class node_dynalloc_state -{ - unknown, - nonnull, - unchecked, - freed -}; - -/* Prefixes to use in SARIF property bags. */ -#define STATE_GRAPH_PREFIX "gcc/diagnostic_state_graph/" -#define STATE_NODE_PREFIX "gcc/diagnostic_state_node/" -#define STATE_EDGE_PREFIX "gcc/diagnostic_state_edge/" - -/* A wrapper around a node that gets/sets attributes, using - the node's property bag for storage, so that the data roundtrips - through SARIF. */ - -struct state_node_ref -{ - state_node_ref (diagnostics::digraphs::node &node) - : m_node (node) - {} - - enum node_kind - get_node_kind () const; - void - set_node_kind (enum node_kind); - - // For node_kind::stack_frame, this will be the function - logical_locations::key - get_logical_loc () const - { - return m_node.get_logical_loc (); - } - - // For node_kind::dynalloc_buffer - enum node_dynalloc_state - get_dynalloc_state () const; - - void - set_dynalloc_state (enum node_dynalloc_state) const; - - const char * - get_dynamic_extents () const; - - const char * - get_name () const { return get_attr ("name"); } - void - set_name (const char *name) const { set_attr ("name", name); } - - const char * - get_type () const { return get_attr ("type"); } - void - set_type (const char *type) const { set_attr ("type", type); } - - const char * - get_value () const { return get_attr ("value"); } - - const char * - get_index () const { return get_attr ("index"); } - - const char * - get_attr (const char *key) const - { - return m_node.get_attr (STATE_NODE_PREFIX, key); - } - - void - set_attr (const char *key, const char *value) const - { - return m_node.set_attr (STATE_NODE_PREFIX, key, value); - } - - void - set_json_attr (const char *key, std::unique_ptr value) const; - - diagnostics::digraphs::node &m_node; -}; - extern std::unique_ptr make_dot_graph (const diagnostics::digraphs::digraph &state_graph, const logical_locations::manager &logical_loc_mgr); diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 492ca291432..81a495b416f 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -6271,16 +6271,16 @@ These are visible by pressing ``j'' and ``k'' to single-step forward and backward through events. Enabling this option will slow down HTML generation. -@item show-state-diagrams-dot-src=@r{[}yes@r{|}no@r{]} +@item show-graph-dot-src=@r{[}yes@r{|}no@r{]} This is a debugging feature and defaults to @code{no}. -If @code{show-state-diagrams-dot-src=yes} +If @code{show-graph-dot-src=yes} then if @code{show-state-diagrams=yes}, the generated state diagrams will also show the .dot source input to GraphViz used for the diagram. -@item show-state-diagrams-sarif=@r{[}yes@r{|}no@r{]} +@item show-graph-sarif=@r{[}yes@r{|}no@r{]} This is a debugging feature and defaults to @code{no}. -If @code{show-state-diagrams-sarif=yes} +If @code{show-graph-sarif=yes} then if @code{show-state-diagrams=yes}, the generated state diagrams will also show a SARIF representation of the state. diff --git a/gcc/json.cc b/gcc/json.cc index 7153f087a00..14ff76bd036 100644 --- a/gcc/json.cc +++ b/gcc/json.cc @@ -394,6 +394,31 @@ object::set_bool (const char *key, bool v) set (key, new json::literal (v)); } +void +object::set_string (const string_property &property, const char *utf8_value) +{ + set_string (property.m_key.get (), utf8_value); +} + +void +object::set_integer (const integer_property &property, long value) +{ + set_integer (property.m_key.get (), value); +} + +void +object::set_bool (const bool_property &property, bool value) +{ + set_bool (property.m_key.get (), value); +} + +void +object::set_array_of_string (const array_of_string_property &property, + std::unique_ptr value) +{ + set (property.m_key.get (), std::move (value)); +} + /* Subroutine of json::compare for comparing a pairs of objects. */ int diff --git a/gcc/json.h b/gcc/json.h index c706f2a4fe9..c53715ecb2c 100644 --- a/gcc/json.h +++ b/gcc/json.h @@ -21,6 +21,8 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_JSON_H #define GCC_JSON_H +#include "label-text.h" + /* Implementation of JSON, a lightweight data-interchange format. See http://www.json.org/ @@ -116,6 +118,41 @@ struct token } // namespace json::pointer +/* Typesafe way to work with properties in JSON objects. */ + +template +struct property +{ + explicit property (const char *key) + : m_key (label_text::borrow (key)) + {} + + explicit property (const char *key_prefix, const char *key) + : m_key (label_text::take (concat (key_prefix, key, nullptr))) + {} + + label_text m_key; +}; + +using string_property = property; +using integer_property = property; +using bool_property = property; +using json_property = property; +using array_of_string_property = property; + +template +struct enum_traits +{ + typedef EnumType enum_t; + + static enum_t get_unknown_value (); + static bool maybe_get_value_from_string (const char *, enum_t &out); + static const char *get_string_for_value (enum_t value); +}; + +template +using enum_property = property>; + /* Base class of JSON value. */ class value @@ -130,6 +167,8 @@ class value void DEBUG_FUNCTION dump () const; virtual object *dyn_cast_object () { return nullptr; } + virtual array *dyn_cast_array () { return nullptr; } + virtual integer_number *dyn_cast_integer_number () { return nullptr; } virtual string *dyn_cast_string () { return nullptr; } static int compare (const json::value &val_a, const json::value &val_b); @@ -183,6 +222,19 @@ class object : public value /* Set to literal true/false. */ void set_bool (const char *key, bool v); + /* Typesafe access to properties by name (such as from a schema). */ + void set_string (const string_property &property, const char *utf8_value); + void set_integer (const integer_property &property, long value); + void set_bool (const bool_property &property, bool value); + void set_array_of_string (const array_of_string_property &property, + std::unique_ptr value); + template + bool maybe_get_enum (const enum_property &property, + EnumType &out) const; + template + void set_enum (const enum_property &property, + EnumType value); + static int compare (const json::object &obj_a, const json::object &obj_b); size_t get_num_keys () const { return m_keys.length (); } @@ -210,6 +262,8 @@ class array : public value void print (pretty_printer *pp, bool formatted) const final override; std::unique_ptr clone () const final override; + array *dyn_cast_array () final override { return this; } + void append (value *v); void append_string (const char *utf8_value); @@ -269,6 +323,8 @@ class integer_number : public value void print (pretty_printer *pp, bool formatted) const final override; std::unique_ptr clone () const final override; + integer_number *dyn_cast_integer_number () final override { return this; } + long get () const { return m_value; } private: @@ -317,6 +373,32 @@ class literal : public value enum kind m_kind; }; + +template +inline bool +object::maybe_get_enum (const enum_property &property, + EnumType &out) const +{ + if (value *jv = get (property.m_key.get ())) + if (string *jstr = jv->dyn_cast_string ()) + { + if (enum_traits::maybe_get_value_from_string + (jstr->get_string (), out)) + return true; + } + return false; +} + +template +inline void +object::set_enum (const enum_property &property, + EnumType value) +{ + const char *str + = json::enum_traits::get_string_for_value (value); + set_string (property.m_key.get (), str); +} + } // namespace json template <> diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_graphs.cc b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_graphs.cc index 7398a290825..8ba576ec81c 100644 --- a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_graphs.cc +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_graphs.cc @@ -210,9 +210,8 @@ report_diag_with_graphs (location_t loc) g->set_description (desc); auto a = std::make_unique (*g, "a"); auto b = std::make_unique (*g, "b"); -#define KEY_PREFIX "/placeholder-prefix/" - b->set_attr (KEY_PREFIX, "color", "red"); -#undef KEY_PREFIX + const json::string_property color ("/placeholder-prefix/color"); + b->set_property (color, "red"); auto c = std::make_unique (*g, "c"); c->set_label ("I am a node label");