mirror of git://gcc.gnu.org/git/gcc.git
diagnostics: generalize state graph code to use json::property instances (v2)
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. This is an updated version of the patch, tested to build with GCC 5 (which the previous version didn't leading to PR bootstrap/122151) 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 <dmalcolm@redhat.com>
This commit is contained in:
parent
822a139e7d
commit
c89bd48e7e
|
@ -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
|
||||
|
|
|
@ -1861,6 +1861,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 \
|
||||
|
@ -1880,7 +1882,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 \
|
||||
|
|
|
@ -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<diagnostics::digraphs::node>
|
||||
analyzer_state_graph::
|
||||
make_state_node (diagnostics::state_graphs::node_kind kind,
|
||||
make_state_node (enum node_properties::kind_t 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);
|
||||
node->set_property (node_properties::kind_prop, kind);
|
||||
return node;
|
||||
}
|
||||
|
||||
std::unique_ptr<diagnostics::digraphs::node>
|
||||
analyzer_state_graph::
|
||||
make_memspace_state_node (const region ®,
|
||||
diagnostics::state_graphs::node_kind kind)
|
||||
enum node_properties::kind_t 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<const frame_region &> (reg);
|
||||
|
||||
node = make_state_node (diagnostics::state_graphs::node_kind::stack_frame,
|
||||
node = make_state_node (node_properties::kind_t::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_t::globals);
|
||||
break;
|
||||
case RK_CODE:
|
||||
node = make_memspace_state_node (reg,
|
||||
diagnostics::state_graphs::node_kind::code);
|
||||
node_properties::kind_t::code);
|
||||
break;
|
||||
case RK_FUNCTION:
|
||||
node = make_memspace_state_node (reg,
|
||||
diagnostics::state_graphs::node_kind::function);
|
||||
node_properties::kind_t::function);
|
||||
// TODO
|
||||
break;
|
||||
|
||||
case RK_STACK:
|
||||
node = make_memspace_state_node (reg,
|
||||
diagnostics::state_graphs::node_kind::stack);
|
||||
node_properties::kind_t::stack);
|
||||
break;
|
||||
case RK_HEAP:
|
||||
node = make_memspace_state_node (reg,
|
||||
diagnostics::state_graphs::node_kind::heap_);
|
||||
node_properties::kind_t::heap_);
|
||||
break;
|
||||
case RK_THREAD_LOCAL:
|
||||
node = make_memspace_state_node (reg,
|
||||
diagnostics::state_graphs::node_kind::thread_local_);
|
||||
node_properties::kind_t::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_t::other);
|
||||
break;
|
||||
|
||||
case RK_DECL:
|
||||
{
|
||||
node = make_state_node (diagnostics::state_graphs::node_kind::variable,
|
||||
node = make_state_node (node_properties::kind_t::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 ());
|
||||
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,15 @@ 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_t::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
|
||||
= make_memspace_state_node (reg,
|
||||
node_properties::kind_t::dynalloc_buffer);
|
||||
set_attr_for_dynamic_extents (reg, *node);
|
||||
break;
|
||||
}
|
||||
|
@ -425,9 +443,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 +473,18 @@ create_state_nodes_for_binding_cluster (const binding_cluster &cluster,
|
|||
std::unique_ptr<diagnostics::digraphs::node>
|
||||
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_t::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_t::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 +509,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 +569,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_t::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 +580,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 +602,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_t::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 +616,27 @@ populate_state_node_for_typed_region (state_node_ref node,
|
|||
= m_mgr.get_field_region (®,
|
||||
const_cast<tree> (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_t::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 +646,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 +659,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;
|
||||
|
|
|
@ -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<diagnostics::digraphs::node>
|
||||
make_state_node (diagnostics::state_graphs::node_kind kind,
|
||||
make_state_node (enum state_node_properties::kind_t kind,
|
||||
std::string id);
|
||||
|
||||
std::unique_ptr<diagnostics::digraphs::node>
|
||||
make_memspace_state_node (const region ®,
|
||||
enum diagnostics::state_graphs::node_kind kind);
|
||||
enum state_node_properties::kind_t kind);
|
||||
|
||||
std::unique_ptr<diagnostics::digraphs::node>
|
||||
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<const region *, diagnostics::digraphs::node *> m_region_to_state_node_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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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_t
|
||||
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_t::unknown;
|
||||
|
||||
case RS_ASSUMED_NON_NULL:
|
||||
return diagnostics::state_graphs::node_dynalloc_state::nonnull;
|
||||
return state_node_properties::dynalloc_state_t::nonnull;
|
||||
|
||||
case RS_UNCHECKED:
|
||||
return diagnostics::state_graphs::node_dynalloc_state::unchecked;
|
||||
return state_node_properties::dynalloc_state_t::unchecked;
|
||||
case RS_NONNULL:
|
||||
return diagnostics::state_graphs::node_dynalloc_state::nonnull;
|
||||
return state_node_properties::dynalloc_state_t::nonnull;
|
||||
case RS_FREED:
|
||||
return diagnostics::state_graphs::node_dynalloc_state::freed;
|
||||
return state_node_properties::dynalloc_state_t::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_prop,
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36891,7 +36891,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 ;;
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/* Extra properties for digraphs in SARIF property bags.
|
||||
Copyright (C) 2025 Free Software Foundation, Inc.
|
||||
Contributed by David Malcolm <dmalcolm@redhat.com>.
|
||||
|
||||
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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#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");
|
|
@ -0,0 +1,37 @@
|
|||
/* Extra properties for digraphs in SARIF property bags.
|
||||
Copyright (C) 2025 Free Software Foundation, Inc.
|
||||
Contributed by David Malcolm <dmalcolm@redhat.com>.
|
||||
|
||||
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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#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 */
|
|
@ -0,0 +1,161 @@
|
|||
/* Properties for capturing state graphs in SARIF property bags.
|
||||
Copyright (C) 2025 Free Software Foundation, Inc.
|
||||
Contributed by David Malcolm <dmalcolm@redhat.com>.
|
||||
|
||||
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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#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<enum node::kind_t>
|
||||
node::kind_prop (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<enum node::dynalloc_state_t>
|
||||
node::dynalloc_state_prop (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_t
|
||||
|
||||
namespace json {
|
||||
|
||||
template<>
|
||||
enum node::kind_t
|
||||
json::enum_traits<enum node::kind_t>::get_unknown_value ()
|
||||
{
|
||||
return node::kind_t::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<enum node::kind_t>::
|
||||
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<enum_t> (i);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<>
|
||||
const char *
|
||||
json::enum_traits<enum node::kind_t>::get_string_for_value (enum_t value)
|
||||
{
|
||||
return node_kind_strs[static_cast<int> (value)];
|
||||
}
|
||||
|
||||
// Traits for enum node:dynalloc_state_t
|
||||
|
||||
template<>
|
||||
enum node::dynalloc_state_t
|
||||
json::enum_traits<enum node::dynalloc_state_t>::get_unknown_value ()
|
||||
{
|
||||
return node::dynalloc_state_t::unknown;
|
||||
}
|
||||
|
||||
static const char * const dynalloc_state_strs[] = {
|
||||
"unknown",
|
||||
"nonnull",
|
||||
"unchecked",
|
||||
"freed"
|
||||
};
|
||||
|
||||
template<>
|
||||
bool
|
||||
json::enum_traits<enum node::dynalloc_state_t>::
|
||||
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<enum_t> (i);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<>
|
||||
const char *
|
||||
json::enum_traits<enum node::dynalloc_state_t>::
|
||||
get_string_for_value (enum_t value)
|
||||
{
|
||||
return dynalloc_state_strs[static_cast <size_t> (value)];
|
||||
}
|
||||
|
||||
} // namespace json
|
|
@ -0,0 +1,98 @@
|
|||
/* Properties for capturing state graphs in SARIF property bags.
|
||||
Copyright (C) 2025 Free Software Foundation, Inc.
|
||||
Contributed by David Malcolm <dmalcolm@redhat.com>.
|
||||
|
||||
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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#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_t
|
||||
{
|
||||
// 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_t
|
||||
{
|
||||
unknown,
|
||||
nonnull,
|
||||
unchecked,
|
||||
freed
|
||||
};
|
||||
|
||||
extern const json::enum_property<enum kind_t> kind_prop;
|
||||
|
||||
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<enum dynalloc_state_t>
|
||||
dynalloc_state_prop;
|
||||
}
|
||||
namespace edge {
|
||||
extern const char *const prefix;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* ! GCC_DIAGNOSTICS_SARIF_PROPERTIES_STATE_GRAPHS_H */
|
|
@ -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 ();
|
||||
|
|
|
@ -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. */
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
set_json_attr (key_prefix, key, std::make_unique<json::string> (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
|
||||
diagnostics::digraphs::object::
|
||||
set_json_attr (const char *key_prefix, const char *key, std::unique_ptr<json::value> value)
|
||||
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
|
||||
{
|
||||
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
|
||||
digraph_object::set_property (const json::json_property &property,
|
||||
std::unique_ptr<json::value> 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<json::object> ();
|
||||
m_property_bag->set (prefixed_key.c_str (), std::move (value));
|
||||
m_property_bag = std::make_unique<sarif_property_bag> ( );
|
||||
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<json::object>
|
||||
diagnostics::digraphs::digraph::make_json_sarif_graph () const
|
||||
digraph::make_json_sarif_graph () const
|
||||
{
|
||||
return make_sarif_graph (*this, nullptr, nullptr);
|
||||
}
|
||||
|
||||
std::unique_ptr<dot::graph>
|
||||
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>
|
||||
diagnostics::digraphs::digraph::clone () const
|
||||
std::unique_ptr<digraph>
|
||||
digraph::clone () const
|
||||
{
|
||||
auto result = std::make_unique<diagnostics::digraphs::digraph> ();
|
||||
|
||||
if (get_property_bag ())
|
||||
result->set_property_bag (get_property_bag ()->clone_as_object ());
|
||||
|
||||
std::map<diagnostics::digraphs::node *, diagnostics::digraphs::node *> node_mapping;
|
||||
std::map<digraph_node *, digraph_node *> 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<digraph_edge> (*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<json::object>
|
||||
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>
|
||||
diagnostics::digraphs::node::clone (digraph &new_graph,
|
||||
std::map<node *, node *> &node_mapping) const
|
||||
std::unique_ptr<digraph_node>
|
||||
digraph_node::clone (digraph &new_graph,
|
||||
std::map<node *, node *> &node_mapping) const
|
||||
{
|
||||
auto result
|
||||
= std::make_unique<diagnostics::digraphs::node> (new_graph,
|
||||
get_id ());
|
||||
= std::make_unique<digraph_node> (new_graph, get_id ());
|
||||
node_mapping.insert ({const_cast <node *> (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<digraph> ();
|
||||
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<digraph_node> (*g, "a");
|
||||
auto b = std::make_unique<digraph_node> (*g, "b");
|
||||
b->set_attr (KEY_PREFIX, "color", "red");
|
||||
b->set_property (json::string_property (KEY_PREFIX, "color"), "red");
|
||||
auto c = std::make_unique<digraph_node> (*g, "c");
|
||||
c->set_label ("I am a node label");
|
||||
|
||||
auto e = std::make_unique<digraph_edge> (*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_prop),
|
||||
state_node_properties::kind_t::other);
|
||||
node.set_property (state_node_properties::kind_prop,
|
||||
state_node_properties::kind_t::stack);
|
||||
ASSERT_EQ (node.get_property (state_node_properties::kind_prop),
|
||||
state_node_properties::kind_t::stack);
|
||||
|
||||
ASSERT_EQ (node.get_property (state_node_properties::dynalloc_state_prop),
|
||||
state_node_properties::dynalloc_state_t::unknown);
|
||||
node.set_property (state_node_properties::dynalloc_state_prop,
|
||||
state_node_properties::dynalloc_state_t::freed);
|
||||
ASSERT_EQ (node.get_property (state_node_properties::dynalloc_state_prop),
|
||||
state_node_properties::dynalloc_state_t::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
|
||||
|
|
|
@ -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);
|
||||
|
||||
void
|
||||
set_attr (const char *key_prefix,
|
||||
const char *key,
|
||||
const char *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 <typename EnumType>
|
||||
EnumType
|
||||
get_property (const json::enum_property<EnumType> &property) const
|
||||
{
|
||||
if (m_property_bag)
|
||||
{
|
||||
EnumType result;
|
||||
if (m_property_bag->maybe_get_enum<EnumType> (property, result))
|
||||
return result;
|
||||
}
|
||||
return json::enum_traits<EnumType>::get_unknown_value ();
|
||||
}
|
||||
template <typename EnumType>
|
||||
void
|
||||
set_json_attr (const char *key_prefix,
|
||||
const char *key,
|
||||
std::unique_ptr<json::value> value);
|
||||
set_property (const json::enum_property<EnumType> &property,
|
||||
EnumType value)
|
||||
{
|
||||
auto &bag = ensure_property_bag ();
|
||||
bag.set_enum<EnumType> (property, 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<json::value> value);
|
||||
|
||||
json::object *
|
||||
get_property_bag () const { return m_property_bag.get (); }
|
||||
|
||||
json::object &
|
||||
ensure_property_bag ();
|
||||
|
||||
void
|
||||
set_property_bag (std::unique_ptr<json::object> property_bag)
|
||||
{
|
||||
|
@ -188,6 +223,9 @@ class digraph : public object
|
|||
|
||||
std::unique_ptr<digraph> 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 *, node *> &node_mapping) const;
|
||||
|
||||
private:
|
||||
private:
|
||||
std::string m_id;
|
||||
std::unique_ptr<std::string> m_label;
|
||||
std::vector<std::unique_ptr<node>> m_children;
|
||||
|
|
|
@ -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<xml::element> ("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<xml::element> ("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<xml::element> ("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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<const char *> &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
|
||||
|
|
|
@ -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_t 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_t::unknown:
|
||||
case state_node_properties::dynalloc_state_t::nonnull:
|
||||
return nullptr;
|
||||
|
||||
case node_dynalloc_state::unchecked:
|
||||
case state_node_properties::dynalloc_state_t::unchecked:
|
||||
return "#ec7a08"; // pf-orange-400
|
||||
|
||||
case node_dynalloc_state::freed:
|
||||
case state_node_properties::dynalloc_state_t::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_t 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::subgraph> (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<dot::edge_stmt> (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_prop));
|
||||
|
||||
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_prop))
|
||||
{
|
||||
default:
|
||||
return false;
|
||||
|
||||
case node_kind::stack:
|
||||
case state_node_properties::kind_t::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_t::dynalloc_buffer:
|
||||
case state_node_properties::kind_t::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_prop))
|
||||
{
|
||||
default:
|
||||
return nullptr;
|
||||
|
||||
case node_kind::globals:
|
||||
case state_node_properties::kind_t::globals:
|
||||
return _("Globals");
|
||||
case node_kind::code:
|
||||
case state_node_properties::kind_t::code:
|
||||
return _("Code");
|
||||
case node_kind::stack:
|
||||
case state_node_properties::kind_t::stack:
|
||||
return _("Stack");
|
||||
case node_kind::heap_:
|
||||
case state_node_properties::kind_t::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_t dynalloc_state)
|
||||
{
|
||||
xp.push_tag ("tr", true);
|
||||
xp.push_tag ("td", false);
|
||||
|
@ -298,48 +302,51 @@ 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_prop);
|
||||
|
||||
switch (input_node_kind)
|
||||
{
|
||||
case node_kind::padding:
|
||||
case node_kind::other:
|
||||
case state_node_properties::kind_t::padding:
|
||||
case state_node_properties::kind_t::other:
|
||||
return;
|
||||
|
||||
case node_kind::stack:
|
||||
case state_node_properties::kind_t::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_t::unknown);
|
||||
break;
|
||||
case node_kind::stack_frame:
|
||||
case state_node_properties::kind_t::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_t::unknown);
|
||||
break;
|
||||
case node_kind::dynalloc_buffer:
|
||||
case state_node_properties::kind_t::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_t dynalloc_st
|
||||
= state_node.get_property
|
||||
(state_node_properties::dynalloc_state_prop);
|
||||
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_t::unknown:
|
||||
case state_node_properties::dynalloc_state_t::nonnull:
|
||||
if (type)
|
||||
{
|
||||
if (extents)
|
||||
|
@ -356,7 +363,7 @@ private:
|
|||
}
|
||||
break;
|
||||
|
||||
case node_dynalloc_state::unchecked:
|
||||
case state_node_properties::dynalloc_state_t::unchecked:
|
||||
if (type)
|
||||
{
|
||||
if (extents)
|
||||
|
@ -371,7 +378,7 @@ private:
|
|||
}
|
||||
break;
|
||||
|
||||
case node_dynalloc_state::freed:
|
||||
case state_node_properties::dynalloc_state_t::freed:
|
||||
// TODO: show deallocator
|
||||
// TODO: show deallocation event
|
||||
pp_printf (&pp, "Freed buffer");
|
||||
|
@ -404,9 +411,10 @@ private:
|
|||
{
|
||||
default:
|
||||
break;
|
||||
case node_kind::variable:
|
||||
case state_node_properties::kind_t::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 +424,10 @@ private:
|
|||
xp.pop_tag ("td");
|
||||
}
|
||||
break;
|
||||
case node_kind::element:
|
||||
case state_node_properties::kind_t::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 +439,10 @@ private:
|
|||
xp.pop_tag ("td");
|
||||
}
|
||||
break;
|
||||
case node_kind::field:
|
||||
case state_node_properties::kind_t::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 +455,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 +466,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 +478,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 +510,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 +520,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 +530,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 +540,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 +548,11 @@ private:
|
|||
const logical_locations::manager &m_logical_loc_mgr;
|
||||
|
||||
/* All nodes involved in edges (and thus will need a port). */
|
||||
std::set<digraphs::node *> m_src_nodes;
|
||||
std::set<digraphs::node *> m_dst_nodes;
|
||||
std::set<const digraphs::node *> m_src_nodes;
|
||||
std::set<const digraphs::node *> m_dst_nodes;
|
||||
|
||||
std::map<digraphs::node *, dot::node_id> m_src_node_to_port_id;
|
||||
std::map<digraphs::node *, dot::node_id> m_dst_node_to_port_id;
|
||||
std::map<const digraphs::node *, dot::node_id> m_src_node_to_port_id;
|
||||
std::map<const digraphs::node *, dot::node_id> m_dst_node_to_port_id;
|
||||
};
|
||||
|
||||
std::unique_ptr<dot::graph>
|
||||
|
|
|
@ -1,156 +0,0 @@
|
|||
/* Extensions to diagnostics::digraphs to support state graphs.
|
||||
Copyright (C) 2025 Free Software Foundation, Inc.
|
||||
Contributed by David Malcolm <dmalcolm@redhat.com>.
|
||||
|
||||
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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#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<int> (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<enum node_kind> (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<enum node_dynalloc_state> (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 <size_t> (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<json::value> 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 */
|
|
@ -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<json::value> value) const;
|
||||
|
||||
diagnostics::digraphs::node &m_node;
|
||||
};
|
||||
|
||||
extern std::unique_ptr<dot::graph>
|
||||
make_dot_graph (const diagnostics::digraphs::digraph &state_graph,
|
||||
const logical_locations::manager &logical_loc_mgr);
|
||||
|
|
|
@ -6296,16 +6296,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.
|
||||
|
||||
|
|
25
gcc/json.cc
25
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<json::array> value)
|
||||
{
|
||||
set<array> (property.m_key.get (), std::move (value));
|
||||
}
|
||||
|
||||
/* Subroutine of json::compare for comparing a pairs of objects. */
|
||||
|
||||
int
|
||||
|
|
82
gcc/json.h
82
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 <typename Traits>
|
||||
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<string>;
|
||||
using integer_property = property<integer_number>;
|
||||
using bool_property = property<literal>;
|
||||
using json_property = property<value>;
|
||||
using array_of_string_property = property<array>;
|
||||
|
||||
template <typename EnumType>
|
||||
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 <typename EnumType>
|
||||
using enum_property = property<enum_traits<EnumType>>;
|
||||
|
||||
/* 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<json::array> value);
|
||||
template <typename EnumType>
|
||||
bool maybe_get_enum (const enum_property<EnumType> &property,
|
||||
EnumType &out) const;
|
||||
template <typename EnumType>
|
||||
void set_enum (const enum_property<EnumType> &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<value> 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<value> 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 <typename EnumType>
|
||||
inline bool
|
||||
object::maybe_get_enum (const enum_property<EnumType> &property,
|
||||
EnumType &out) const
|
||||
{
|
||||
if (value *jv = get (property.m_key.get ()))
|
||||
if (string *jstr = jv->dyn_cast_string ())
|
||||
{
|
||||
if (enum_traits<EnumType>::maybe_get_value_from_string
|
||||
(jstr->get_string (), out))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename EnumType>
|
||||
inline void
|
||||
object::set_enum (const enum_property<EnumType> &property,
|
||||
EnumType value)
|
||||
{
|
||||
const char *str
|
||||
= json::enum_traits<EnumType>::get_string_for_value (value);
|
||||
set_string (property.m_key.get (), str);
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
|
||||
template <>
|
||||
|
|
|
@ -210,9 +210,8 @@ report_diag_with_graphs (location_t loc)
|
|||
g->set_description (desc);
|
||||
auto a = std::make_unique<diagnostic_node> (*g, "a");
|
||||
auto b = std::make_unique<diagnostic_node> (*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<diagnostic_node> (*g, "c");
|
||||
c->set_label ("I am a node label");
|
||||
|
||||
|
|
Loading…
Reference in New Issue