diff options
Diffstat (limited to 'gcc/diagnostic-state-to-dot.cc')
-rw-r--r-- | gcc/diagnostic-state-to-dot.cc | 537 |
1 files changed, 0 insertions, 537 deletions
diff --git a/gcc/diagnostic-state-to-dot.cc b/gcc/diagnostic-state-to-dot.cc deleted file mode 100644 index ddae83b..0000000 --- a/gcc/diagnostic-state-to-dot.cc +++ /dev/null @@ -1,537 +0,0 @@ -/* Creating GraphViz .dot files from XML state documents. - 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 "xml.h" -#include "xml-printer.h" -#include "graphviz.h" - -static int -get_depth (const xml::element &e) -{ - int deepest_child = 0; - for (auto &iter : e.m_children) - if (xml::element *child_element = iter->dyn_cast_element ()) - deepest_child = std::max (deepest_child, - get_depth (*child_element)); - return deepest_child + 1; -} - -enum class dynalloc_state -{ - unknown, - nonnull, - unchecked, - freed -}; - -static const char * -get_color_for_dynalloc_state (enum dynalloc_state dynalloc_st) -{ - switch (dynalloc_st) - { - default: - gcc_unreachable (); - break; - case dynalloc_state::unknown: - case dynalloc_state::nonnull: - return nullptr; - - case dynalloc_state::unchecked: - return "#ec7a08"; // pf-orange-400 - - case dynalloc_state::freed: - return "#cc0000"; // pf-red-100 - } -} - -static void -set_color_for_dynalloc_state (dot::attr_list &attrs, - enum dynalloc_state dynalloc_state) -{ - if (const char *color = get_color_for_dynalloc_state (dynalloc_state)) - attrs.add (dot::id ("color"), dot::id (color)); -} - -static enum dynalloc_state -get_dynalloc_state (const xml::element &input_element) -{ - const char *dyn_alloc_state = input_element.get_attr ("dynamic-alloc-state"); - if (!dyn_alloc_state) - return dynalloc_state::unknown; - - if (dyn_alloc_state == std::string ("unchecked")) - return dynalloc_state::unchecked; - - if (dyn_alloc_state == std::string ("nonnull")) - return dynalloc_state::nonnull; - - if (dyn_alloc_state == std::string ("freed")) - return dynalloc_state::freed; - - return dynalloc_state::unknown; -} - -class state_diagram : public dot::graph -{ -public: - state_diagram (const xml::document &input_state_doc) - : m_next_id (0), - m_show_tags (false) - { - // "node [shape=plaintext]\n" - { - auto attr_stmt - = std::make_unique<dot::attr_stmt> (dot::attr_stmt::kind::node); - attr_stmt->m_attrs.add (dot::id ("shape"), dot::id ("plaintext")); - add_stmt (std::move (attr_stmt)); - } - - /* Recurse down the XML state diagram, creating subgraphs - and then eventually creating nodes, and recursively - creating XML tables, adding ports for the endpoints of edges, - and recording edges we'll want to create (into m_pending_edges). */ - xml::element *input_elmt_state_diagram - = input_state_doc.find_child_element ("state-diagram"); - gcc_assert (input_elmt_state_diagram); - xml::element *input_elmt_mem_regions - = input_elmt_state_diagram->find_child_element ("memory-regions"); - if (!input_elmt_mem_regions) - return; - auto root_cluster - = std::make_unique<dot::subgraph> (dot::id ("cluster_memory_regions")); - for (auto &iter : input_elmt_mem_regions->m_children) - on_input_xml_node (*root_cluster, *iter); - add_stmt (std::move (root_cluster)); - - /* We should now have ports for edge endpoints for all region ids. - Use them now to create edges. */ - for (auto &pe : m_pending_edges) - { - auto search = m_region_id_to_dst_node_id.find (pe.m_dst_region_id); - if (search != m_region_id_to_dst_node_id.end ()) - { - auto &dst_node_id = search->second; - auto e = std::make_unique<dot::edge_stmt> (pe.m_src_node_id, - dst_node_id); - - auto dynalloc_state = m_region_id_to_dynalloc_state.find (pe.m_dst_region_id); - if (dynalloc_state != m_region_id_to_dynalloc_state.end ()) - set_color_for_dynalloc_state (e->m_attrs, - dynalloc_state->second); - - add_stmt (std::move (e)); - } - } - } - -private: - struct pending_edge - { - dot::node_id m_src_node_id; - std::string m_dst_region_id; - }; - - dot::id - get_id_for_region (const char *region_id) - { - gcc_assert (region_id); - return std::string ("cluster_region_") + region_id; - } - - dot::id - make_id (bool cluster = false) - { - if (cluster) - return std::string ("cluster_") + std::to_string (m_next_id++); - else - return std::string ("id_") + std::to_string (m_next_id++); - } - - bool - starts_node_p (const xml::element &e) - { - if (e.m_kind == "stack" - || e.m_kind == "heap-buffer" - || e.m_kind == "variable") // e.g. within globals - return true; - return false; - } - - void - on_input_xml_node (dot::subgraph &parent_subgraph, - xml::node &input_node) - { - xml::element *input_element = input_node.dyn_cast_element (); - if (!input_element) - return; - - dot::id sg_id = make_id (true); - - if (starts_node_p (*input_element)) - { - // Create node with table - xml::element table ("table", false); - xml::printer xp (table); - xp.set_attr ("border", "0"); - xp.set_attr ("cellborder", "1"); - xp.set_attr ("cellspacing", "0"); - - const int max_depth = get_depth (*input_element); - const int num_columns = max_depth + 2; - - dot::id id_of_node = make_id (); - on_xml_node (id_of_node, xp, *input_element, - max_depth, 0, num_columns); - - auto node = std::make_unique<dot::node_stmt> (std::move (id_of_node)); - node->m_attrs.add (dot::id ("shape"), - dot::id ("plaintext")); - - // xml must be done by now - - node->m_attrs.add (dot::id ("label"), - dot::id (table)); - - parent_subgraph.m_stmt_list.add_stmt (std::move (node)); - } - else - { - auto child_subgraph = std::make_unique<dot::subgraph> (std::move (sg_id)); - - if (const char *label = input_element->get_attr ("label")) - child_subgraph->add_attr (dot::id ("label"), dot::id (label)); - - // recurse: - for (auto &iter : input_element->m_children) - on_input_xml_node (*child_subgraph, *iter); - parent_subgraph.m_stmt_list.add_stmt (std::move (child_subgraph)); - } - } - - enum class style { h1, h2 }; - - void - add_title_tr (const dot::id &id_of_node, - xml::printer &xp, - int num_columns, - const xml::element &input_element, - std::string heading, - enum style styl, - enum dynalloc_state dynalloc_state) - { - xp.push_tag ("tr", true); - xp.push_tag ("td", false); - xp.set_attr ("colspan", std::to_string (num_columns)); - xp.set_attr ("cellpadding", "5"); - - const char *bgcolor; - const char *color; - if (const char *c = get_color_for_dynalloc_state (dynalloc_state)) - { - bgcolor = c; - color = "white"; - } - else - switch (styl) - { - default: - gcc_unreachable (); - case style::h1: - // from diagnostic-format-html.cc: HTML_STYLE .linenum - bgcolor = "#0088ce"; - color = "white"; - break; - case style::h2: - // from diagnostic-format-html.cc: HTML_STYLE .events-hdr - bgcolor = "#393f44"; // pf-black-800 - color = "white"; - break; - } - - xp.set_attr ("bgcolor", bgcolor); - xp.push_tag ("font", false); - xp.set_attr ("color", color); - if (heading == "") - heading = " "; - xp.add_text (std::move (heading)); - xp.pop_tag ("font"); - - maybe_add_dst_port (id_of_node, xp, input_element); - - xp.pop_tag ("td"); - xp.pop_tag ("tr"); - } - - /* Recursively add <TR> to XP for INPUT_NODE and its descendents. */ - void - on_xml_node (const dot::id &id_of_node, - xml::printer &xp, - xml::node &input_node, - int max_depth, - int depth, - int num_columns) - { - bool recurse = true; - - xml::element *input_element = input_node.dyn_cast_element (); - if (!input_element) - return; - - if (input_element->m_kind == "concrete-bindings") - return; - if (input_element->m_kind == "padding") - return; - - if (input_element->m_kind == "stack") - { - add_title_tr (id_of_node, xp, num_columns, *input_element, "Stack", - style::h1, dynalloc_state::unknown); - } - else if (input_element->m_kind == "stack-frame") - { - if (const char *function = input_element->get_attr ("function")) - add_title_tr (id_of_node, xp, num_columns, *input_element, - std::string ("Frame: ") + function, - style::h2, dynalloc_state::unknown); - } - else if (input_element->m_kind == "heap-buffer") - { - const char *extents = input_element->get_attr ("dynamic-extents"); - enum dynalloc_state dynalloc_st = get_dynalloc_state (*input_element); - if (auto region_id = input_element->get_attr ("region_id")) - m_region_id_to_dynalloc_state[region_id] = dynalloc_st; - const char *type = input_element->get_attr ("type"); - pretty_printer pp; - switch (dynalloc_st) - { - default: - gcc_unreachable (); - - case dynalloc_state::unknown: - case dynalloc_state::nonnull: - if (type) - { - if (extents) - pp_printf (&pp, "%s (%s byte allocation)", - type, extents); - else - pp_printf (&pp, "%s", type); - } - else - { - if (extents) - pp_printf (&pp, "%s byte allocation", - extents); - } - break; - - case dynalloc_state::unchecked: - if (type) - { - if (extents) - pp_printf (&pp, "%s (unchecked %s byte allocation)", - type, extents); - } - else - { - if (extents) - pp_printf (&pp, "Unchecked %s byte allocation", - extents); - } - break; - - case dynalloc_state::freed: - // TODO: show deallocator - // TODO: show deallocation event - pp_printf (&pp, "Freed buffer"); - break; - } - add_title_tr (id_of_node, xp, num_columns, *input_element, - pp_formatted_text (&pp), - style::h2, - dynalloc_st); - } - else - { - xp.push_tag ("tr", true); - if (depth > 0) - { - /* Indent, by create a <td> spanning "depth" columns. */ - xp.push_tag ("td", false); - xp.set_attr ("colspan", std::to_string (depth)); - xp.add_text (" "); // graphviz doesn't like <td/> - xp.pop_tag ("td"); - } - if (m_show_tags) - { - // Debug: show XML tag - xp.push_tag ("td", false); - xp.add_text ("<"); - xp.add_text (input_element->m_kind); - xp.add_text (">"); - xp.pop_tag ("td"); - } - if (input_element->m_kind == "variable") - { - const char *name = input_element->get_attr ("name"); - gcc_assert (name); - xp.push_tag ("td", false); - maybe_add_dst_port (id_of_node, xp, *input_element); - push_src_text (xp); - xp.add_text (name); - pop_src_text (xp); - xp.pop_tag ("td"); - } - else if (input_element->m_kind == "element") - { - const char *index = input_element->get_attr ("index"); - gcc_assert (index); - xp.push_tag ("td", false); - maybe_add_dst_port (id_of_node, xp, *input_element); - push_src_text (xp); - xp.add_text ("["); - xp.add_text (index); - xp.add_text ("]"); - pop_src_text (xp); - xp.pop_tag ("td"); - } - else if (input_element->m_kind == "field") - { - const char *name = input_element->get_attr ("name"); - gcc_assert (name); - xp.push_tag ("td", false); - maybe_add_dst_port (id_of_node, xp, *input_element); - push_src_text (xp); - xp.add_text ("."); - xp.add_text (name); - pop_src_text (xp); - xp.pop_tag ("td"); - } - if (const char *type = input_element->get_attr ("type")) - { - xp.push_tag ("td", false); - if (max_depth > depth) - xp.set_attr ("colspan", std::to_string (max_depth - depth)); - xp.set_attr ("align", "right"); - push_src_text (xp); - xp.add_text (type); - pop_src_text (xp); - xp.pop_tag ("td"); - } - if (auto value = input_element->find_child_element ("value-of-region")) - { - xp.push_tag ("td", false); - for (auto &iter : value->m_children) - if (auto child_element = iter->dyn_cast_element ()) - print_value (id_of_node, xp, *child_element); - xp.pop_tag ("td"); - recurse = false; - } - xp.pop_tag ("tr"); - } - - if (recurse) - for (auto &iter : input_element->m_children) - on_xml_node (id_of_node, xp, *iter, max_depth, depth + 1, num_columns); - } - - void - push_src_text (xml::printer &xp) - { - xp.push_tag ("font"); - xp.set_attr ("color", "blue"); - } - - void - pop_src_text (xml::printer &xp) - { - xp.pop_tag ("font"); - } - - void - print_value (const dot::id &id_of_node, - xml::printer &xp, - xml::element &input_element) - { - if (input_element.m_kind == "pointer-to-region") - if (const char *dst_region_id = input_element.get_attr ("region_id")) - { - dot::id src_port_id = make_id (); - xp.set_attr ("port", src_port_id.m_str); - m_pending_edges.push_back - ({dot::node_id (id_of_node, - dot::port (src_port_id, - dot::compass_pt::e)), - dst_region_id}); - } - - if (input_element.m_kind == "uninitialized") - { - xp.add_text ("(uninitialized)"); - return; - } - - if (auto dump_text = input_element.get_attr ("dump-text")) - xp.add_text (dump_text); - } - - /* If INPUT_ELEMENT has a "region_id", add a port to XP for possible - incoming edges to use. */ - - void - maybe_add_dst_port (const dot::id &id_of_node, - xml::printer &xp, - const xml::element &input_element) - { - if (const char *region_id = input_element.get_attr ("region_id")) - { - dot::id dst_id = make_id (); - dot::node_id node_id (id_of_node, - dot::port (dst_id/*, - dot::compass_pt::w*/)); - xp.set_attr ("port", dst_id.m_str); - m_region_id_to_dst_node_id.emplace (std::string (region_id), - std::move (node_id)); - } - } - - -private: - int m_next_id; - std::vector<pending_edge> m_pending_edges; - std::map<std::string, dot::node_id> m_region_id_to_dst_node_id; - std::map<std::string, enum dynalloc_state> m_region_id_to_dynalloc_state; - bool m_show_tags; -}; - -std::unique_ptr<dot::graph> -make_dot_graph_from_xml_state (const xml::document &xml_state) -{ - return std::make_unique<state_diagram> (xml_state); -} |