This patch adds a new text_art::tree_widget, which makes it easy to generate hierarchical visualizations using either ASCII:
+- Child 0 | +- Grandchild 0 0 | +- Grandchild 0 1 | `- Grandchild 0 2 +- Child 1 | +- Grandchild 1 0 | +- Grandchild 1 1 | `- Grandchild 1 2 `- Child 2 +- Grandchild 2 0 +- Grandchild 2 1 `- Grandchild 2 2 or Unicode: Root ├─ Child 0 │ ├─ Grandchild 0 0 │ ├─ Grandchild 0 1 │ ╰─ Grandchild 0 2 ├─ Child 1 │ ├─ Grandchild 1 0 │ ├─ Grandchild 1 1 │ ╰─ Grandchild 1 2 ╰─ Child 2 ├─ Grandchild 2 0 ├─ Grandchild 2 1 ╰─ Grandchild 2 2 potentially with colorization of the connecting lines. It adds a new template for typename T: void text_art::dump<T> (const T&); for using this to dump any object to stderr that supports a make_dump_widget method, with similar templates for dumping to a pretty_printer * and a FILE *. It uses this within the analyzer to add two new families of dumping methods: one for program states, e.g.: (gdb) call state->dump() State ├─ Region Model │ ├─ Current Frame: frame: ‘calls_malloc’@2 │ ├─ Store │ │ ├─ m_called_unknown_fn: false │ │ ├─ frame: ‘test’@1 │ │ │ ╰─ _1: (INIT_VAL(n_2(D))*(size_t)4) │ │ ╰─ frame: ‘calls_malloc’@2 │ │ ├─ result_4: &HEAP_ALLOCATED_REGION(27) │ │ ╰─ _5: &HEAP_ALLOCATED_REGION(27) │ ╰─ Dynamic Extents │ ╰─ HEAP_ALLOCATED_REGION(27): (INIT_VAL(n_2(D))*(size_t)4) ╰─ ‘malloc’ state machine ╰─ 0x468cb40: &HEAP_ALLOCATED_REGION(27): unchecked ({free}) (‘result_4’) and the other for showing the detail of the recursive makeup of svalues and regions, e.g. the (INIT_VAL(n_2(D))*(size_t)4) from above: (gdb) call size_in_bytes->dump() (17): ‘long unsigned int’: binop_svalue(mult_expr: ‘*’) ├─ (15): ‘size_t’: initial_svalue │ ╰─ m_reg: (12): ‘size_t’: decl_region(‘n_2(D)’) │ ╰─ parent: (9): frame_region(‘test’, index: 0, depth: 1) │ ╰─ parent: (1): stack region │ ╰─ parent: (0): root region ╰─ (16): ‘size_t’: constant_svalue (‘4’) I've already found both of these useful when debugging analyzer issues. The patch uses the former to update the output of -fdump-analyzer-exploded-nodes-2 and -fdump-analyzer-exploded-nodes-3. The older dumping functions within the analyzer are retained in case they turn out to still be useful for debugging. Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu. Pushed to trunk as r15-925-g97238e4217d5cd. gcc/ChangeLog: * Makefile.in (OBJS-libcommon): Add text-art/tree-widget.o. * doc/analyzer.texi: Rewrite discussion of dumping state to cover the text_art::tree_widget-based dumps, with a more interesting example. * text-art/dump-widget-info.h: New file. * text-art/dump.h: New file. * text-art/selftests.cc (selftest::text_art_tests): Call text_art_tree_widget_cc_tests. * text-art/selftests.h (selftest::text_art_tree_widget_cc_tests): New decl. * text-art/theme.cc (ascii_theme::get_cppchar): Handle the various cell_kind::TREE_*. (unicode_theme::get_cppchar): Likewise. * text-art/theme.h (enum class theme::cell_kind): Add TREE_CHILD_NON_FINAL, TREE_CHILD_FINAL, TREE_X_CONNECTOR, and TREE_Y_CONNECTOR. * text-art/tree-widget.cc: New file. gcc/analyzer/ChangeLog: * call-details.cc: Define INCLUDE_VECTOR. * call-info.cc: Likewise. * call-summary.cc: Likewise. * checker-event.cc: Likewise. * checker-path.cc: Likewise. * complexity.cc: Likewise. * constraint-manager.cc: Likewise. (bounded_range::make_dump_widget): New. (bounded_ranges::add_to_dump_widget): New. (equiv_class::make_dump_widget): New. (constraint::make_dump_widget): New. (bounded_ranges_constraint::make_dump_widget): New. (constraint_manager::make_dump_widget): New. * constraint-manager.h (bounded_range::make_dump_widget): New decl. (bounded_ranges::add_to_dump_widget): New decl. (equiv_class::make_dump_widget): New decl. (constraint::make_dump_widget): New decl. (bounded_ranges_constraint::make_dump_widget): New decl. (constraint_manager::make_dump_widget): New decl. * diagnostic-manager.cc: Define INCLUDE_VECTOR. * engine.cc: Likewise. Include "text-art/dump.h". (setjmp_svalue::print_dump_widget_label): New. (setjmp_svalue::add_dump_widget_children): New. (exploded_graph::dump_exploded_nodes): Use text_art::dump_to_file for -fdump-analyzer-exploded-nodes-2 and -fdump-analyzer-exploded-nodes-3. Fix overlong line. * feasible-graph.cc: Define INCLUDE_VECTOR. * infinite-recursion.cc: Likewise. * kf-analyzer.cc: Likewise. * kf-lang-cp.cc: Likewise. * kf.cc: Likewise. * known-function-manager.cc: Likewise. * pending-diagnostic.cc: Likewise. * program-point.cc: Likewise. * program-state.cc: Likewise. Include "text-art/tree-widget" and "text-art/dump.h". (sm_state_map::make_dump_widget): New. (program_state::dump): New. (program_state::make_dump_widget): New. * program-state.h: Include "text-art/widget.h". (sm_state_map::make_dump_widget): New decl. (program_state::dump): New decl. (program_state::make_dump_widget): New decl. * ranges.cc: Define INCLUDE_VECTOR. * record-layout.cc: Likewise. * region-model-asm.cc: Likewise. * region-model-manager.cc: Likewise. * region-model-reachability.cc: Likewise. * region-model.cc: Likewise. Include "text-art/tree-widget.h". (region_to_value_map::make_dump_widget): New. (region_model::dump): New. (region_model::make_dump_widget): New. (selftest::test_dump): Add test of dump_to_pp<region_model>. * region-model.h: Include "text-art/widget.h" and "text-art/dump.h". (region_to_value_map::make_dump_widget): New decl. (region_model::dump): New decl. (region_model::make_dump_widget): New decl. * region.cc: Define INCLUDE_VECTOR and include "text-art/dump.h". (region::dump): New. (region::make_dump_widget): New. (region::add_dump_widget_children): New. (frame_region::print_dump_widget_label): New. (globals_region::print_dump_widget_label): New. (code_region::print_dump_widget_label): New. (function_region::print_dump_widget_label): New. (label_region::print_dump_widget_label): New. (stack_region::print_dump_widget_label): New. (heap_region::print_dump_widget_label): New. (root_region::print_dump_widget_label): New. (thread_local_region::print_dump_widget_label): New. (symbolic_region::print_dump_widget_label): New. (symbolic_region::add_dump_widget_children): New. (decl_region::print_dump_widget_label): New. (field_region::print_dump_widget_label): New. (element_region::print_dump_widget_label): New. (element_region::add_dump_widget_children): New. (offset_region::print_dump_widget_label): New. (offset_region::add_dump_widget_children): New. (sized_region::print_dump_widget_label): New. (sized_region::add_dump_widget_children): New. (cast_region::print_dump_widget_label): New. (cast_region::add_dump_widget_children): New. (heap_allocated_region::print_dump_widget_label): New. (alloca_region::print_dump_widget_label): New. (string_region::print_dump_widget_label): New. (bit_range_region::print_dump_widget_label): New. (var_arg_region::print_dump_widget_label): New. (errno_region::print_dump_widget_label): New. (private_region::print_dump_widget_label): New. (unknown_region::print_dump_widget_label): New. * region.h: Include "text-art/widget.h". (region::dump): New decl. (region::make_dump_widget): New decl. (region::add_dump_widget_children): New decl. (frame_region::print_dump_widget_label): New decl. (globals_region::print_dump_widget_label): New decl. (code_region::print_dump_widget_label): New decl. (function_region::print_dump_widget_label): New decl. (label_region::print_dump_widget_label): New decl. (stack_region::print_dump_widget_label): New decl. (heap_region::print_dump_widget_label): New decl. (root_region::print_dump_widget_label): New decl. (thread_local_region::print_dump_widget_label): New decl. (symbolic_region::print_dump_widget_label): New decl. (symbolic_region::add_dump_widget_children): New decl. (decl_region::print_dump_widget_label): New decl. (field_region::print_dump_widget_label): New decl. (element_region::print_dump_widget_label): New decl. (element_region::add_dump_widget_children): New decl. (offset_region::print_dump_widget_label): New decl. (offset_region::add_dump_widget_children): New decl. (sized_region::print_dump_widget_label): New decl. (sized_region::add_dump_widget_children): New decl. (cast_region::print_dump_widget_label): New decl. (cast_region::add_dump_widget_children): New decl. (heap_allocated_region::print_dump_widget_label): New decl. (alloca_region::print_dump_widget_label): New decl. (string_region::print_dump_widget_label): New decl. (bit_range_region::print_dump_widget_label): New decl. (var_arg_region::print_dump_widget_label): New decl. (errno_region::print_dump_widget_label): New decl. (private_region::print_dump_widget_label): New decl. (unknown_region::print_dump_widget_label): New decl. * sm-fd.cc: Define INCLUDE_VECTOR. * sm-file.cc: Likewise. * sm-malloc.cc: Likewise. * sm-pattern-test.cc: Likewise. * sm-signal.cc: Likewise. * sm-taint.cc: Likewise. * sm.cc: Likewise. * state-purge.cc: Likewise. * store.cc: Likewise. Include "text-art/tree-widget.h". (add_binding_to_tree_widget): New. (binding_map::add_to_tree_widget): New. (binding_cluster::make_dump_widget): New. (store::make_dump_widget): New. * store.h: Include "text-art/tree-widget.h". (binding_map::add_to_tree_widget): New decl. (binding_cluster::make_dump_widget): New decl. (store::make_dump_widget): New decl. * svalue.cc: Define INCLUDE_VECTOR. Include "make-unique.h" and "text-art/dump.h". (svalue::dump): New. (svalue::make_dump_widget): New. (region_svalue::print_dump_widget_label): New. (region_svalue::add_dump_widget_children): New. (constant_svalue::print_dump_widget_label): New. (constant_svalue::add_dump_widget_children): New. (unknown_svalue::print_dump_widget_label): New. (unknown_svalue::add_dump_widget_children): New. (poisoned_svalue::print_dump_widget_label): New. (poisoned_svalue::add_dump_widget_children): New. (initial_svalue::print_dump_widget_label): New. (initial_svalue::add_dump_widget_children): New. (unaryop_svalue::print_dump_widget_label): New. (unaryop_svalue::add_dump_widget_children): New. (binop_svalue::print_dump_widget_label): New. (binop_svalue::add_dump_widget_children): New. (sub_svalue::print_dump_widget_label): New. (sub_svalue::add_dump_widget_children): New. (repeated_svalue::print_dump_widget_label): New. (repeated_svalue::add_dump_widget_children): New. (bits_within_svalue::print_dump_widget_label): New. (bits_within_svalue::add_dump_widget_children): New. (widening_svalue::print_dump_widget_label): New. (widening_svalue::add_dump_widget_children): New. (placeholder_svalue::print_dump_widget_label): New. (placeholder_svalue::add_dump_widget_children): New. (unmergeable_svalue::print_dump_widget_label): New. (unmergeable_svalue::add_dump_widget_children): New. (compound_svalue::print_dump_widget_label): New. (compound_svalue::add_dump_widget_children): New. (conjured_svalue::print_dump_widget_label): New. (conjured_svalue::add_dump_widget_children): New. (asm_output_svalue::print_dump_widget_label): New. (asm_output_svalue::add_dump_widget_children): New. (const_fn_result_svalue::print_dump_widget_label): New. (const_fn_result_svalue::add_dump_widget_children): New. * svalue.h: Include "text-art/widget.h". Add "using text_art::dump_widget_info". (svalue::dump): New decl. (svalue::make_dump_widget): New decl. (svalue::print_dump_widget_label): New decl. (svalue::print_dump_widget_label): New decl. (svalue::add_dump_widget_children): New decl. (region_svalue::print_dump_widget_label): New decl. (region_svalue::add_dump_widget_children): New decl. (constant_svalue::print_dump_widget_label): New decl. (constant_svalue::add_dump_widget_children): New decl. (unknown_svalue::print_dump_widget_label): New decl. (unknown_svalue::add_dump_widget_children): New decl. (poisoned_svalue::print_dump_widget_label): New decl. (poisoned_svalue::add_dump_widget_children): New decl. (initial_svalue::print_dump_widget_label): New decl. (initial_svalue::add_dump_widget_children): New decl. (unaryop_svalue::print_dump_widget_label): New decl. (unaryop_svalue::add_dump_widget_children): New decl. (binop_svalue::print_dump_widget_label): New decl. (binop_svalue::add_dump_widget_children): New decl. (sub_svalue::print_dump_widget_label): New decl. (sub_svalue::add_dump_widget_children): New decl. (repeated_svalue::print_dump_widget_label): New decl. (repeated_svalue::add_dump_widget_children): New decl. (bits_within_svalue::print_dump_widget_label): New decl. (bits_within_svalue::add_dump_widget_children): New decl. (widening_svalue::print_dump_widget_label): New decl. (widening_svalue::add_dump_widget_children): New decl. (placeholder_svalue::print_dump_widget_label): New decl. (placeholder_svalue::add_dump_widget_children): New decl. (unmergeable_svalue::print_dump_widget_label): New decl. (unmergeable_svalue::add_dump_widget_children): New decl. (compound_svalue::print_dump_widget_label): New decl. (compound_svalue::add_dump_widget_children): New decl. (conjured_svalue::print_dump_widget_label): New decl. (conjured_svalue::add_dump_widget_children): New decl. (asm_output_svalue::print_dump_widget_label): New decl. (asm_output_svalue::add_dump_widget_children): New decl. (const_fn_result_svalue::print_dump_widget_label): New decl. (const_fn_result_svalue::add_dump_widget_children): New decl. * trimmed-graph.cc: Define INCLUDE_VECTOR. * varargs.cc: Likewise. gcc/testsuite/ChangeLog: * gcc.dg/plugin/analyzer_cpython_plugin.c: Define INCLUDE_VECTOR. * gcc.dg/plugin/analyzer_gil_plugin.c: Likewise. * gcc.dg/plugin/analyzer_kernel_plugin.c: Likewise. * gcc.dg/plugin/analyzer_known_fns_plugin.c: Likewise. Signed-off-by: David Malcolm <dmalc...@redhat.com> --- gcc/Makefile.in | 1 + gcc/analyzer/call-details.cc | 1 + gcc/analyzer/call-info.cc | 1 + gcc/analyzer/call-summary.cc | 1 + gcc/analyzer/checker-event.cc | 1 + gcc/analyzer/checker-path.cc | 1 + gcc/analyzer/complexity.cc | 1 + gcc/analyzer/constraint-manager.cc | 100 +++++ gcc/analyzer/constraint-manager.h | 20 + gcc/analyzer/diagnostic-manager.cc | 1 + gcc/analyzer/engine.cc | 29 +- gcc/analyzer/feasible-graph.cc | 1 + gcc/analyzer/infinite-recursion.cc | 1 + gcc/analyzer/kf-analyzer.cc | 1 + gcc/analyzer/kf-lang-cp.cc | 1 + gcc/analyzer/kf.cc | 1 + gcc/analyzer/known-function-manager.cc | 1 + gcc/analyzer/pending-diagnostic.cc | 1 + gcc/analyzer/program-point.cc | 1 + gcc/analyzer/program-state.cc | 112 +++++ gcc/analyzer/program-state.h | 10 + gcc/analyzer/ranges.cc | 1 + gcc/analyzer/record-layout.cc | 1 + gcc/analyzer/region-model-asm.cc | 1 + gcc/analyzer/region-model-manager.cc | 1 + gcc/analyzer/region-model-reachability.cc | 1 + gcc/analyzer/region-model.cc | 78 ++++ gcc/analyzer/region-model.h | 9 + gcc/analyzer/region.cc | 251 +++++++++++ gcc/analyzer/region.h | 68 +++ gcc/analyzer/sm-fd.cc | 1 + gcc/analyzer/sm-file.cc | 1 + gcc/analyzer/sm-malloc.cc | 1 + gcc/analyzer/sm-pattern-test.cc | 1 + gcc/analyzer/sm-signal.cc | 1 + gcc/analyzer/sm-taint.cc | 1 + gcc/analyzer/sm.cc | 1 + gcc/analyzer/state-purge.cc | 1 + gcc/analyzer/store.cc | 156 +++++++ gcc/analyzer/store.h | 13 + gcc/analyzer/svalue.cc | 421 ++++++++++++++++++ gcc/analyzer/svalue.h | 143 +++++- gcc/analyzer/trimmed-graph.cc | 1 + gcc/analyzer/varargs.cc | 1 + gcc/doc/analyzer.texi | 175 ++++++-- .../gcc.dg/plugin/analyzer_cpython_plugin.c | 1 + .../gcc.dg/plugin/analyzer_gil_plugin.c | 1 + .../gcc.dg/plugin/analyzer_kernel_plugin.c | 1 + .../gcc.dg/plugin/analyzer_known_fns_plugin.c | 1 + gcc/text-art/dump-widget-info.h | 53 +++ gcc/text-art/dump.h | 83 ++++ gcc/text-art/selftests.cc | 1 + gcc/text-art/selftests.h | 1 + gcc/text-art/theme.cc | 18 + gcc/text-art/theme.h | 8 +- gcc/text-art/tree-widget.cc | 237 ++++++++++ gcc/text-art/tree-widget.h | 83 ++++ 57 files changed, 2071 insertions(+), 33 deletions(-) create mode 100644 gcc/text-art/dump-widget-info.h create mode 100644 gcc/text-art/dump.h create mode 100644 gcc/text-art/tree-widget.cc create mode 100644 gcc/text-art/tree-widget.h diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 66d42cc41f84..e200c1310647 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1836,6 +1836,7 @@ OBJS-libcommon = diagnostic-spec.o diagnostic.o diagnostic-color.o \ text-art/styled-string.o \ text-art/table.o \ text-art/theme.o \ + text-art/tree-widget.o \ text-art/widget.o # Objects in libcommon-target.a, used by drivers and by the core diff --git a/gcc/analyzer/call-details.cc b/gcc/analyzer/call-details.cc index ca47953f1461..fda925edb968 100644 --- a/gcc/analyzer/call-details.cc +++ b/gcc/analyzer/call-details.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" diff --git a/gcc/analyzer/call-info.cc b/gcc/analyzer/call-info.cc index 4c965b2136c0..828ece5c7f03 100644 --- a/gcc/analyzer/call-info.cc +++ b/gcc/analyzer/call-info.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" diff --git a/gcc/analyzer/call-summary.cc b/gcc/analyzer/call-summary.cc index c2c9c71f79b4..60ca78a334da 100644 --- a/gcc/analyzer/call-summary.cc +++ b/gcc/analyzer/call-summary.cc @@ -19,6 +19,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" diff --git a/gcc/analyzer/checker-event.cc b/gcc/analyzer/checker-event.cc index b64c58ef7702..ee3ceb407ea1 100644 --- a/gcc/analyzer/checker-event.cc +++ b/gcc/analyzer/checker-event.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" diff --git a/gcc/analyzer/checker-path.cc b/gcc/analyzer/checker-path.cc index dc041ec88232..c8361915ad9c 100644 --- a/gcc/analyzer/checker-path.cc +++ b/gcc/analyzer/checker-path.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" diff --git a/gcc/analyzer/complexity.cc b/gcc/analyzer/complexity.cc index f6caa23235cc..0f1f8cad5798 100644 --- a/gcc/analyzer/complexity.cc +++ b/gcc/analyzer/complexity.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" diff --git a/gcc/analyzer/constraint-manager.cc b/gcc/analyzer/constraint-manager.cc index 06a2bb860e63..707385d3fa67 100644 --- a/gcc/analyzer/constraint-manager.cc +++ b/gcc/analyzer/constraint-manager.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" @@ -462,6 +463,14 @@ bounded_range::to_json () const return range_obj; } +std::unique_ptr<text_art::widget> +bounded_range::make_dump_widget (const text_art::dump_widget_info &dwi) const +{ + using text_art::tree_widget; + return tree_widget::from_fmt (dwi, default_tree_printer, + "%qE ... %qE", m_lower, m_upper); +} + /* Subroutine of bounded_range::to_json. */ void @@ -729,6 +738,14 @@ bounded_ranges::to_json () const return arr_obj; } +void +bounded_ranges::add_to_dump_widget (text_art::tree_widget &parent, + const text_art::dump_widget_info &dwi) const +{ + for (auto &range : m_ranges) + parent.add_child (range.make_dump_widget (dwi)); +} + /* Determine whether (X OP RHS_CONST) is known to be true or false for all X in the ranges expressed by this object. */ @@ -1129,6 +1146,39 @@ equiv_class::to_json () const return ec_obj; } +std::unique_ptr<text_art::widget> +equiv_class::make_dump_widget (const text_art::dump_widget_info &dwi, + unsigned id) const +{ + using text_art::tree_widget; + std::unique_ptr<tree_widget> ec_widget; + + { + pretty_printer pp; + pp_string (&pp, "Equivalence class "); + equiv_class_id (id).print (&pp); + ec_widget = tree_widget::make (dwi, &pp); + } + + for (const svalue *sval : m_vars) + { + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + sval->dump_to_pp (&pp, true); + ec_widget->add_child (tree_widget::make (dwi, &pp)); + } + + if (m_constant) + { + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + pp_printf (&pp, "%qE", m_constant); + ec_widget->add_child (tree_widget::make (dwi, &pp)); + } + + return ec_widget; +} + /* Generate a hash value for this equiv_class. This relies on the ordering of m_vars, and so this object needs to have been canonicalized for this to be meaningful. */ @@ -1354,6 +1404,17 @@ constraint::to_json () const return con_obj; } +std::unique_ptr<text_art::widget> +constraint::make_dump_widget (const text_art::dump_widget_info &dwi, + const constraint_manager &cm) const +{ + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + pp_show_color (&pp) = true; + print (&pp, cm); + return text_art::tree_widget::make (dwi, &pp); +} + /* Generate a hash value for this constraint. */ hashval_t @@ -1430,6 +1491,18 @@ bounded_ranges_constraint::to_json () const return con_obj; } +std::unique_ptr<text_art::widget> +bounded_ranges_constraint:: +make_dump_widget (const text_art::dump_widget_info &dwi) const +{ + using text_art::tree_widget; + std::unique_ptr<tree_widget> brc_widget + (tree_widget::from_fmt (dwi, nullptr, + "ec%i bounded ranges", m_ec_id.as_int ())); + m_ranges->add_to_dump_widget (*brc_widget.get (), dwi); + return brc_widget; +} + bool bounded_ranges_constraint:: operator== (const bounded_ranges_constraint &other) const @@ -1756,6 +1829,33 @@ constraint_manager::to_json () const return cm_obj; } +std::unique_ptr<text_art::widget> +constraint_manager::make_dump_widget (const text_art::dump_widget_info &dwi) const +{ + using text_art::tree_widget; + std::unique_ptr<tree_widget> cm_widget + (tree_widget::make (dwi, "Constraints")); + + /* Equivalence classes. */ + unsigned i; + equiv_class *ec; + FOR_EACH_VEC_ELT (m_equiv_classes, i, ec) + cm_widget->add_child (ec->make_dump_widget (dwi, i)); + + /* Constraints. */ + for (const constraint &c : m_constraints) + cm_widget->add_child (c.make_dump_widget (dwi, *this)); + + /* m_bounded_ranges_constraints. */ + for (const auto &brc : m_bounded_ranges_constraints) + cm_widget->add_child (brc.make_dump_widget (dwi)); + + if (cm_widget->get_num_children () == 0) + return nullptr; + + return cm_widget; +} + /* Attempt to add the constraint LHS OP RHS to this constraint_manager. Return true if the constraint could be added (or is already true). Return false if the constraint contradicts existing knowledge. */ diff --git a/gcc/analyzer/constraint-manager.h b/gcc/analyzer/constraint-manager.h index 3b8189d6b732..31556aebc7a1 100644 --- a/gcc/analyzer/constraint-manager.h +++ b/gcc/analyzer/constraint-manager.h @@ -87,6 +87,9 @@ struct bounded_range json::object *to_json () const; + std::unique_ptr<text_art::widget> + make_dump_widget (const text_art::dump_widget_info &dwi) const; + bool contains_p (tree cst) const; bool intersects_p (const bounded_range &other, @@ -134,6 +137,9 @@ public: json::value *to_json () const; + void add_to_dump_widget (text_art::tree_widget &parent, + const text_art::dump_widget_info &dwi) const; + tristate eval_condition (enum tree_code op, tree rhs_const, bounded_ranges_manager *mgr) const; @@ -267,6 +273,10 @@ public: json::object *to_json () const; + std::unique_ptr<text_art::widget> + make_dump_widget (const text_art::dump_widget_info &dwi, + unsigned id) const; + bool contains_non_constant_p () const; /* An equivalence class can contain multiple constants (e.g. multiple @@ -343,6 +353,10 @@ class constraint json::object *to_json () const; + std::unique_ptr<text_art::widget> + make_dump_widget (const text_art::dump_widget_info &dwi, + const constraint_manager &cm) const; + hashval_t hash () const; bool operator== (const constraint &other) const; @@ -394,6 +408,9 @@ public: void add_to_hash (inchash::hash *hstate) const; + std::unique_ptr<text_art::widget> + make_dump_widget (const text_art::dump_widget_info &dwi) const; + equiv_class_id m_ec_id; const bounded_ranges *m_ranges; }; @@ -427,6 +444,9 @@ public: json::object *to_json () const; + std::unique_ptr<text_art::widget> + make_dump_widget (const text_art::dump_widget_info &dwi) const; + const equiv_class &get_equiv_class_by_index (unsigned idx) const { return *m_equiv_classes[idx]; diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc index 08d92f9780e7..da98b9679cbb 100644 --- a/gcc/analyzer/diagnostic-manager.cc +++ b/gcc/analyzer/diagnostic-manager.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc index 556e8a1c8288..46d9b80f4f7e 100644 --- a/gcc/analyzer/engine.cc +++ b/gcc/analyzer/engine.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "make-unique.h" @@ -68,6 +69,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-dfa.h" #include "analyzer/known-function-manager.h" #include "analyzer/call-summary.h" +#include "text-art/dump.h" /* For an overview, see gcc/doc/analyzer.texi. */ @@ -263,6 +265,26 @@ setjmp_svalue::dump_to_pp (pretty_printer *pp, bool simple) const pp_printf (pp, "setjmp_svalue(EN%i)", get_enode_index ()); } +/* Implementation of svalue::print_dump_widget_label vfunc for + setjmp_svalue. */ + +void +setjmp_svalue::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "setjmp_svalue(EN: %i)", get_enode_index ()); +} + +/* Implementation of svalue::add_dump_widget_children vfunc for + setjmp_svalue. */ + +void +setjmp_svalue:: +add_dump_widget_children (text_art::tree_widget &, + const text_art::dump_widget_info &) const +{ + /* No children. */ +} + /* Get the index of the stored exploded_node. */ int @@ -5420,7 +5442,7 @@ exploded_graph::dump_exploded_nodes () const pretty_printer pp; enode->get_point ().print (&pp, format (true)); fprintf (outf, "%s\n", pp_formatted_text (&pp)); - enode->get_state ().dump_to_file (m_ext_state, false, true, outf); + text_art::dump_to_file (enode->get_state (), outf); } fclose (outf); @@ -5439,7 +5461,8 @@ exploded_graph::dump_exploded_nodes () const = xasprintf ("%s.en-%i.txt", dump_base_name, i); FILE *outf = fopen (filename, "w"); if (!outf) - error_at (UNKNOWN_LOCATION, "unable to open %qs for writing", filename); + error_at (UNKNOWN_LOCATION, "unable to open %qs for writing", + filename); free (filename); fprintf (outf, "EN %i:\n", enode->m_index); @@ -5447,7 +5470,7 @@ exploded_graph::dump_exploded_nodes () const pretty_printer pp; enode->get_point ().print (&pp, format (true)); fprintf (outf, "%s\n", pp_formatted_text (&pp)); - enode->get_state ().dump_to_file (m_ext_state, false, true, outf); + text_art::dump_to_file (enode->get_state (), outf); fclose (outf); } diff --git a/gcc/analyzer/feasible-graph.cc b/gcc/analyzer/feasible-graph.cc index b475c7855a0d..a1812231572c 100644 --- a/gcc/analyzer/feasible-graph.cc +++ b/gcc/analyzer/feasible-graph.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" diff --git a/gcc/analyzer/infinite-recursion.cc b/gcc/analyzer/infinite-recursion.cc index 65f136ddad6b..6103c2b564fb 100644 --- a/gcc/analyzer/infinite-recursion.cc +++ b/gcc/analyzer/infinite-recursion.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" diff --git a/gcc/analyzer/kf-analyzer.cc b/gcc/analyzer/kf-analyzer.cc index a7fb1bce9806..4c7302bd844e 100644 --- a/gcc/analyzer/kf-analyzer.cc +++ b/gcc/analyzer/kf-analyzer.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" diff --git a/gcc/analyzer/kf-lang-cp.cc b/gcc/analyzer/kf-lang-cp.cc index d42495eb9d53..8d1c566c5451 100644 --- a/gcc/analyzer/kf-lang-cp.cc +++ b/gcc/analyzer/kf-lang-cp.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" diff --git a/gcc/analyzer/kf.cc b/gcc/analyzer/kf.cc index c60e220dd1b9..4213b89ac9fb 100644 --- a/gcc/analyzer/kf.cc +++ b/gcc/analyzer/kf.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" diff --git a/gcc/analyzer/known-function-manager.cc b/gcc/analyzer/known-function-manager.cc index d24e5b8f4079..522d2aa9e577 100644 --- a/gcc/analyzer/known-function-manager.cc +++ b/gcc/analyzer/known-function-manager.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" diff --git a/gcc/analyzer/pending-diagnostic.cc b/gcc/analyzer/pending-diagnostic.cc index b2961c21bfc8..ff37ae6f7b43 100644 --- a/gcc/analyzer/pending-diagnostic.cc +++ b/gcc/analyzer/pending-diagnostic.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" diff --git a/gcc/analyzer/program-point.cc b/gcc/analyzer/program-point.cc index 6e225cfe7254..14ce5be4bcf9 100644 --- a/gcc/analyzer/program-point.cc +++ b/gcc/analyzer/program-point.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc index 7842b596a4b2..dc2d4bdf7b0b 100644 --- a/gcc/analyzer/program-state.cc +++ b/gcc/analyzer/program-state.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" @@ -53,6 +54,8 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/state-purge.h" #include "analyzer/call-summary.h" #include "analyzer/analyzer-selftests.h" +#include "text-art/tree-widget.h" +#include "text-art/dump.h" #if ENABLE_ANALYZER @@ -303,6 +306,85 @@ sm_state_map::to_json () const return map_obj; } +/* Make a text_art::tree_widget describing this sm_state_map, + using MODEL if non-null to describe svalues. */ + +std::unique_ptr<text_art::widget> +sm_state_map::make_dump_widget (const text_art::dump_widget_info &dwi, + const region_model *model) const +{ + using text_art::styled_string; + using text_art::tree_widget; + std::unique_ptr<tree_widget> state_widget + (tree_widget::from_fmt (dwi, nullptr, + "%qs state machine", m_sm.get_name ())); + + if (m_global_state != m_sm.get_start_state ()) + { + pretty_printer the_pp; + pretty_printer * const pp = &the_pp; + pp_format_decoder (pp) = default_tree_printer; + pp_string (pp, "Global State: "); + m_global_state->dump_to_pp (pp); + state_widget->add_child (tree_widget::make (dwi, pp)); + } + + auto_vec <const svalue *> keys (m_map.elements ()); + for (map_t::iterator iter = m_map.begin (); + iter != m_map.end (); + ++iter) + keys.quick_push ((*iter).first); + keys.qsort (svalue::cmp_ptr_ptr); + unsigned i; + const svalue *sval; + FOR_EACH_VEC_ELT (keys, i, sval) + { + pretty_printer the_pp; + pretty_printer * const pp = &the_pp; + const bool simple = true; + pp_format_decoder (pp) = default_tree_printer; + if (!flag_dump_noaddr) + { + pp_pointer (pp, sval); + pp_string (pp, ": "); + } + sval->dump_to_pp (pp, simple); + + entry_t e = *const_cast <map_t &> (m_map).get (sval); + pp_string (pp, ": "); + e.m_state->dump_to_pp (pp); + if (model) + if (tree rep = model->get_representative_tree (sval)) + { + pp_string (pp, " ("); + dump_quoted_tree (pp, rep); + pp_character (pp, ')'); + } + if (e.m_origin) + { + pp_string (pp, " (origin: "); + if (!flag_dump_noaddr) + { + pp_pointer (pp, e.m_origin); + pp_string (pp, ": "); + } + e.m_origin->dump_to_pp (pp, simple); + if (model) + if (tree rep = model->get_representative_tree (e.m_origin)) + { + pp_string (pp, " ("); + dump_quoted_tree (pp, rep); + pp_character (pp, ')'); + } + pp_string (pp, ")"); + } + + state_widget->add_child (tree_widget::make (dwi, pp)); + } + + return state_widget; +} + /* Return true if no states have been set within this map (all expressions are for the start state). */ @@ -1101,6 +1183,14 @@ program_state::dump (const extrinsic_state &ext_state, dump_to_file (ext_state, summarize, true, stderr); } +/* Dump a tree-like representation of this state to stderr. */ + +DEBUG_FUNCTION void +program_state::dump () const +{ + text_art::dump (*this); +} + /* Return a new json::object of the form {"store" : object for store, "constraints" : object for constraint_manager, @@ -1138,6 +1228,28 @@ program_state::to_json (const extrinsic_state &ext_state) const return state_obj; } + +std::unique_ptr<text_art::widget> +program_state::make_dump_widget (const text_art::dump_widget_info &dwi) const +{ + using text_art::tree_widget; + std::unique_ptr<tree_widget> state_widget + (tree_widget::from_fmt (dwi, nullptr, "State")); + + state_widget->add_child (m_region_model->make_dump_widget (dwi)); + + /* Add nodes for any sm_state_maps with state. */ + { + int i; + sm_state_map *smap; + FOR_EACH_VEC_ELT (m_checker_states, i, smap) + if (!smap->is_empty_p ()) + state_widget->add_child (smap->make_dump_widget (dwi, m_region_model)); + } + + return state_widget; +} + /* Update this program_state to reflect a top-level call to FUN. The params will have initial_svalues. */ diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h index 3ba6a931cd6f..7e751386b21e 100644 --- a/gcc/analyzer/program-state.h +++ b/gcc/analyzer/program-state.h @@ -21,6 +21,8 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_ANALYZER_PROGRAM_STATE_H #define GCC_ANALYZER_PROGRAM_STATE_H +#include "text-art/widget.h" + namespace ana { /* Data shared by all program_state instances. */ @@ -117,6 +119,10 @@ public: json::object *to_json () const; + std::unique_ptr<text_art::widget> + make_dump_widget (const text_art::dump_widget_info &dwi, + const region_model *model) const; + bool is_empty_p () const; hashval_t hash () const; @@ -223,9 +229,13 @@ public: void dump_to_file (const extrinsic_state &ext_state, bool simple, bool multiline, FILE *outf) const; void dump (const extrinsic_state &ext_state, bool simple) const; + void dump () const; json::object *to_json (const extrinsic_state &ext_state) const; + std::unique_ptr<text_art::widget> + make_dump_widget (const text_art::dump_widget_info &dwi) const; + void push_frame (const extrinsic_state &ext_state, const function &fun); const function * get_current_function () const; diff --git a/gcc/analyzer/ranges.cc b/gcc/analyzer/ranges.cc index 659ada7609d6..f591efae43af 100644 --- a/gcc/analyzer/ranges.cc +++ b/gcc/analyzer/ranges.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" diff --git a/gcc/analyzer/record-layout.cc b/gcc/analyzer/record-layout.cc index 567dfd9809a6..af3115518489 100644 --- a/gcc/analyzer/record-layout.cc +++ b/gcc/analyzer/record-layout.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" diff --git a/gcc/analyzer/region-model-asm.cc b/gcc/analyzer/region-model-asm.cc index a88c3538d964..2a074b647755 100644 --- a/gcc/analyzer/region-model-asm.cc +++ b/gcc/analyzer/region-model-asm.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" diff --git a/gcc/analyzer/region-model-manager.cc b/gcc/analyzer/region-model-manager.cc index f155eeb87c0d..b094b2f7e434 100644 --- a/gcc/analyzer/region-model-manager.cc +++ b/gcc/analyzer/region-model-manager.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" diff --git a/gcc/analyzer/region-model-reachability.cc b/gcc/analyzer/region-model-reachability.cc index 2a1df312d21a..b9887902980f 100644 --- a/gcc/analyzer/region-model-reachability.cc +++ b/gcc/analyzer/region-model-reachability.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index 0dd5671db1be..e71c6ec693b4 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -21,6 +21,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY #define INCLUDE_ALGORITHM +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "make-unique.h" @@ -80,6 +81,7 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/feasible-graph.h" #include "analyzer/record-layout.h" #include "diagnostic-format-sarif.h" +#include "text-art/tree-widget.h" #if ENABLE_ANALYZER @@ -256,6 +258,39 @@ region_to_value_map::to_json () const return map_obj; } +std::unique_ptr<text_art::widget> +region_to_value_map:: +make_dump_widget (const text_art::dump_widget_info &dwi) const +{ + if (is_empty ()) + return nullptr; + + std::unique_ptr<text_art::tree_widget> w + (text_art::tree_widget::make (dwi, "Dynamic Extents")); + + auto_vec<const region *> regs; + for (iterator iter = begin (); iter != end (); ++iter) + regs.safe_push ((*iter).first); + regs.qsort (region::cmp_ptr_ptr); + + unsigned i; + const region *reg; + FOR_EACH_VEC_ELT (regs, i, reg) + { + pretty_printer the_pp; + pretty_printer * const pp = &the_pp; + pp_format_decoder (pp) = default_tree_printer; + const bool simple = true; + + reg->dump_to_pp (pp, simple); + pp_string (pp, ": "); + const svalue *sval = *get (reg); + sval->dump_to_pp (pp, true); + w->add_child (text_art::tree_widget::make (dwi, pp)); + } + return w; +} + /* Attempt to merge THIS with OTHER, writing the result to OUT. @@ -465,6 +500,14 @@ region_model::dump (bool simple) const dump (stderr, simple, true); } +/* Dump a tree-like representation of this state to stderr. */ + +DEBUG_FUNCTION void +region_model::dump () const +{ + text_art::dump (*this); +} + /* Dump a multiline representation of this model to stderr. */ DEBUG_FUNCTION void @@ -489,6 +532,33 @@ region_model::to_json () const return model_obj; } +std::unique_ptr<text_art::widget> +region_model::make_dump_widget (const text_art::dump_widget_info &dwi) const +{ + using text_art::tree_widget; + std::unique_ptr<tree_widget> model_widget + (tree_widget::from_fmt (dwi, nullptr, "Region Model")); + + if (m_current_frame) + { + pretty_printer the_pp; + pretty_printer * const pp = &the_pp; + pp_format_decoder (pp) = default_tree_printer; + pp_show_color (pp) = true; + const bool simple = true; + + pp_string (pp, "Current Frame: "); + m_current_frame->dump_to_pp (pp, simple); + model_widget->add_child (tree_widget::make (dwi, pp)); + } + model_widget->add_child + (m_store.make_dump_widget (dwi, + m_mgr->get_store_manager ())); + model_widget->add_child (m_constraints->make_dump_widget (dwi)); + model_widget->add_child (m_dynamic_extents.make_dump_widget (dwi)); + return model_widget; +} + /* Assert that this object is valid. */ void @@ -7307,6 +7377,14 @@ test_dump () "constraint_manager:\n" " equiv classes:\n" " constraints:\n"); + + text_art::ascii_theme theme; + pretty_printer pp; + dump_to_pp (model, &theme, &pp); + ASSERT_STREQ ("Region Model\n" + "`- Store\n" + " `- m_called_unknown_fn: false\n", + pp_formatted_text (&pp)); } /* Helper function for selftests. Create a struct or union type named NAME, diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h index 118e0f254273..400d80b25683 100644 --- a/gcc/analyzer/region-model.h +++ b/gcc/analyzer/region-model.h @@ -35,6 +35,8 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/known-function-manager.h" #include "analyzer/region-model-manager.h" #include "analyzer/pending-diagnostic.h" +#include "text-art/widget.h" +#include "text-art/dump.h" using namespace ana; @@ -177,6 +179,9 @@ public: json::object *to_json () const; + std::unique_ptr<text_art::widget> + make_dump_widget (const text_art::dump_widget_info &dwi) const; + bool can_merge_with_p (const region_to_value_map &other, region_to_value_map *out) const; @@ -277,11 +282,15 @@ class region_model void dump_to_pp (pretty_printer *pp, bool simple, bool multiline) const; void dump (FILE *fp, bool simple, bool multiline) const; void dump (bool simple) const; + void dump () const; void debug () const; json::object *to_json () const; + std::unique_ptr<text_art::widget> + make_dump_widget (const text_art::dump_widget_info &dwi) const; + void validate () const; void canonicalize (); diff --git a/gcc/analyzer/region.cc b/gcc/analyzer/region.cc index 7d79b45563fd..71bae97b6f11 100644 --- a/gcc/analyzer/region.cc +++ b/gcc/analyzer/region.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" @@ -57,6 +58,7 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/region-model.h" #include "analyzer/sm.h" #include "analyzer/program-state.h" +#include "text-art/dump.h" #if ENABLE_ANALYZER @@ -1040,6 +1042,23 @@ region::dump (bool simple) const pp_flush (&pp); } +/* Dump a tree-like representation of this region and its constituent symbols + to stderr, using global_dc's colorization and theming options. + + For example: + . (gdb) call reg->dump() + . (26): ‘int’: decl_region(‘x_10(D)’) + . ╰─ parent: (9): frame_region(‘test_bitmask_2’, index: 0, depth: 1) + . ╰─ parent: (1): stack region + . ╰─ parent: (0): root region + */ + +DEBUG_FUNCTION void +region::dump () const +{ + text_art::dump (*this); +} + /* Return a new json::string describing the region. */ json::value * @@ -1072,6 +1091,44 @@ region::maybe_print_for_user (pretty_printer *pp, return false; } +/* Use DWI to create a text_art::widget describing this region in + a tree-like form, using PREFIX as a prefix (e.g. for field names). */ + +std::unique_ptr<text_art::widget> +region::make_dump_widget (const text_art::dump_widget_info &dwi, + const char *prefix) const +{ + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + pp_show_color (&pp) = true; + + if (prefix) + pp_printf (&pp, "%s: ", prefix); + + pp_printf (&pp, "(%i): ", get_id ()); + if (get_type ()) + pp_printf (&pp, "%qT: ", get_type ()); + + print_dump_widget_label (&pp); + + std::unique_ptr<text_art::tree_widget> w + (text_art::tree_widget::make (dwi, &pp)); + + add_dump_widget_children (*w, dwi); + + if (m_parent) + w->add_child (m_parent->make_dump_widget (dwi, "parent")); + + return w; +} + +void +region::add_dump_widget_children (text_art::tree_widget &, + const text_art::dump_widget_info &) const +{ + /* By default, add nothing (parent is added in make_dump_widget). */ +} + /* Generate a description of this region. */ DEBUG_FUNCTION label_text @@ -1341,6 +1398,13 @@ frame_region::dump_to_pp (pretty_printer *pp, bool simple) const function_name (&m_fun), m_index, get_stack_depth ()); } +void +frame_region::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "frame_region(%qs, index: %i, depth: %i)", + function_name (&m_fun), m_index, get_stack_depth ()); +} + const decl_region * frame_region::get_region_for_local (region_model_manager *mgr, tree expr, @@ -1411,6 +1475,12 @@ globals_region::dump_to_pp (pretty_printer *pp, bool simple) const pp_string (pp, "globals"); } +void +globals_region::print_dump_widget_label (pretty_printer *pp) const +{ + pp_string (pp, "globals"); +} + /* class code_region : public map_region. */ /* Implementation of region::dump_to_pp vfunc for code_region. */ @@ -1424,6 +1494,12 @@ code_region::dump_to_pp (pretty_printer *pp, bool simple) const pp_string (pp, "code_region()"); } +void +code_region::print_dump_widget_label (pretty_printer *pp) const +{ + pp_string (pp, "code region"); +} + /* class function_region : public region. */ /* Implementation of region::dump_to_pp vfunc for function_region. */ @@ -1443,6 +1519,14 @@ function_region::dump_to_pp (pretty_printer *pp, bool simple) const } } +void +function_region::print_dump_widget_label (pretty_printer *pp) const +{ + pp_string (pp, "function_region("); + dump_quoted_tree (pp, m_fndecl); + pp_string (pp, ")"); +} + /* class label_region : public region. */ /* Implementation of region::dump_to_pp vfunc for label_region. */ @@ -1462,6 +1546,14 @@ label_region::dump_to_pp (pretty_printer *pp, bool simple) const } } +void +label_region::print_dump_widget_label (pretty_printer *pp) const +{ + pp_string (pp, "label_region("); + dump_quoted_tree (pp, m_label); + pp_string (pp, ")"); +} + /* class stack_region : public region. */ /* Implementation of region::dump_to_pp vfunc for stack_region. */ @@ -1475,6 +1567,12 @@ stack_region::dump_to_pp (pretty_printer *pp, bool simple) const pp_string (pp, "stack_region()"); } +void +stack_region::print_dump_widget_label (pretty_printer *pp) const +{ + pp_string (pp, "stack region"); +} + /* class heap_region : public region. */ /* Implementation of region::dump_to_pp vfunc for heap_region. */ @@ -1488,6 +1586,12 @@ heap_region::dump_to_pp (pretty_printer *pp, bool simple) const pp_string (pp, "heap_region()"); } +void +heap_region::print_dump_widget_label (pretty_printer *pp) const +{ + pp_string (pp, "heap_region"); +} + /* class root_region : public region. */ /* root_region's ctor. */ @@ -1508,6 +1612,12 @@ root_region::dump_to_pp (pretty_printer *pp, bool simple) const pp_string (pp, "root_region()"); } +void +root_region::print_dump_widget_label (pretty_printer *pp) const +{ + pp_string (pp, "root region"); +} + /* class thread_local_region : public space_region. */ void @@ -1519,6 +1629,12 @@ thread_local_region::dump_to_pp (pretty_printer *pp, bool simple) const pp_string (pp, "thread_local_region()"); } +void +thread_local_region::print_dump_widget_label (pretty_printer *pp) const +{ + pp_string (pp, "thread_local_region"); +} + /* class symbolic_region : public map_region. */ /* symbolic_region's ctor. */ @@ -1568,6 +1684,20 @@ symbolic_region::dump_to_pp (pretty_printer *pp, bool simple) const } } +void +symbolic_region::print_dump_widget_label (pretty_printer *pp) const +{ + pp_string (pp, "symbolic_region: %<*%>"); +} + +void +symbolic_region:: +add_dump_widget_children (text_art::tree_widget &w, + const text_art::dump_widget_info &dwi) const +{ + w.add_child (m_sval_ptr->make_dump_widget (dwi, "m_sval_ptr")); +} + /* class decl_region : public region. */ /* Implementation of region::dump_to_pp vfunc for decl_region. */ @@ -1587,6 +1717,12 @@ decl_region::dump_to_pp (pretty_printer *pp, bool simple) const } } +void +decl_region::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "decl_region(%qE)", m_decl); +} + /* Get the stack depth for the frame containing this decl, or 0 for a global. */ @@ -1816,6 +1952,12 @@ field_region::dump_to_pp (pretty_printer *pp, bool simple) const } } +void +field_region::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "field_region(%qE)", m_field); +} + /* Implementation of region::get_relative_concrete_offset vfunc for field_region. */ @@ -1892,6 +2034,20 @@ element_region::dump_to_pp (pretty_printer *pp, bool simple) const } } +void +element_region::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "element_region: %<[]%>"); +} + +void +element_region:: +add_dump_widget_children (text_art::tree_widget &w, + const text_art::dump_widget_info &dwi) const +{ + w.add_child (m_index->make_dump_widget (dwi, "m_index")); +} + /* Implementation of region::get_relative_concrete_offset vfunc for element_region. */ @@ -1980,6 +2136,20 @@ offset_region::dump_to_pp (pretty_printer *pp, bool simple) const } } +void +offset_region::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "offset_region"); +} + +void +offset_region:: +add_dump_widget_children (text_art::tree_widget &w, + const text_art::dump_widget_info &dwi) const +{ + w.add_child (m_byte_offset->make_dump_widget (dwi, "m_byte_offset")); +} + const svalue * offset_region::get_bit_offset (region_model_manager *mgr) const { @@ -2053,6 +2223,20 @@ sized_region::dump_to_pp (pretty_printer *pp, bool simple) const } } +void +sized_region::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "sized_region"); +} + +void +sized_region:: +add_dump_widget_children (text_art::tree_widget &w, + const text_art::dump_widget_info &dwi) const +{ + w.add_child (m_byte_size_sval->make_dump_widget (dwi, "m_byte_size_sval")); +} + /* Implementation of region::get_byte_size vfunc for sized_region. */ bool @@ -2124,6 +2308,21 @@ cast_region::dump_to_pp (pretty_printer *pp, bool simple) const } } +void +cast_region::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "cast_region"); +} + +void +cast_region:: +add_dump_widget_children (text_art::tree_widget &w, + const text_art::dump_widget_info &dwi) const +{ + w.add_child + (m_original_region->make_dump_widget (dwi, "m_original_region")); +} + /* Implementation of region::get_relative_concrete_offset vfunc for cast_region. */ @@ -2147,6 +2346,12 @@ heap_allocated_region::dump_to_pp (pretty_printer *pp, bool simple) const pp_printf (pp, "heap_allocated_region(%i)", get_id ()); } +void +heap_allocated_region::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "heap_allocated_region"); +} + /* class alloca_region : public region. */ /* Implementation of region::dump_to_pp vfunc for alloca_region. */ @@ -2160,6 +2365,12 @@ alloca_region::dump_to_pp (pretty_printer *pp, bool simple) const pp_printf (pp, "alloca_region(%i)", get_id ()); } +void +alloca_region::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "alloca_region"); +} + /* class string_region : public region. */ /* Implementation of region::dump_to_pp vfunc for string_region. */ @@ -2182,6 +2393,14 @@ string_region::dump_to_pp (pretty_printer *pp, bool simple) const } } +void +string_region::print_dump_widget_label (pretty_printer *pp) const +{ + pp_string (pp, "string_region("); + dump_tree (pp, m_string_cst); + pp_string (pp, ")"); +} + /* class bit_range_region : public region. */ /* Implementation of region::dump_to_pp vfunc for bit_range_region. */ @@ -2207,6 +2426,14 @@ bit_range_region::dump_to_pp (pretty_printer *pp, bool simple) const } } +void +bit_range_region::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "bit_range_region(m_bits: "); + m_bits.dump_to_pp (pp); + pp_string (pp, ")"); +} + /* Implementation of region::get_byte_size vfunc for bit_range_region. */ bool @@ -2293,6 +2520,12 @@ var_arg_region::dump_to_pp (pretty_printer *pp, bool simple) const } } +void +var_arg_region::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "var_arg_region(arg_idx: %i)", m_idx); +} + /* Get the frame_region for this var_arg_region. */ const frame_region * @@ -2313,6 +2546,12 @@ errno_region::dump_to_pp (pretty_printer *pp, bool simple) const pp_string (pp, "errno_region()"); } +void +errno_region::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "errno_region"); +} + /* class private_region : public region. */ void @@ -2324,6 +2563,12 @@ private_region::dump_to_pp (pretty_printer *pp, bool simple) const pp_printf (pp, "private_region(%qs)", m_desc); } +void +private_region::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "private_region(%qs)", m_desc); +} + /* class unknown_region : public region. */ /* Implementation of region::dump_to_pp vfunc for unknown_region. */ @@ -2334,6 +2579,12 @@ unknown_region::dump_to_pp (pretty_printer *pp, bool /*simple*/) const pp_string (pp, "UNKNOWN_REGION"); } +void +unknown_region::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "unknown_region"); +} + } // namespace ana #endif /* #if ENABLE_ANALYZER */ diff --git a/gcc/analyzer/region.h b/gcc/analyzer/region.h index 0c190c2f0a2e..5d58abc26938 100644 --- a/gcc/analyzer/region.h +++ b/gcc/analyzer/region.h @@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see #define GCC_ANALYZER_REGION_H #include "analyzer/symbol.h" +#include "text-art/widget.h" namespace ana { @@ -173,12 +174,18 @@ public: virtual void dump_to_pp (pretty_printer *pp, bool simple) const = 0; void dump (bool simple) const; + void dump () const; json::value *to_json () const; + bool maybe_print_for_user (pretty_printer *pp, const region_model &model) const; + std::unique_ptr<text_art::widget> + make_dump_widget (const text_art::dump_widget_info &dwi, + const char *prefix = nullptr) const; + bool non_null_p () const; static int cmp_ptr_ptr (const void *, const void *); @@ -257,6 +264,12 @@ public: region_offset calc_offset (region_model_manager *mgr) const; const svalue *calc_initial_value_at_main (region_model_manager *mgr) const; + virtual void + print_dump_widget_label (pretty_printer *pp) const = 0; + virtual void + add_dump_widget_children (text_art::tree_widget &, + const text_art::dump_widget_info &dwi) const; + const region *m_parent; tree m_type; @@ -357,6 +370,8 @@ public: void accept (visitor *v) const final override; void dump_to_pp (pretty_printer *pp, bool simple) const final override; + void print_dump_widget_label (pretty_printer *pp) const final override; + /* Accessors. */ const frame_region *get_calling_frame () const { return m_calling_frame; } const function &get_function () const { return m_fun; } @@ -416,6 +431,7 @@ class globals_region : public space_region /* region vfuncs. */ enum region_kind get_kind () const final override { return RK_GLOBALS; } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + void print_dump_widget_label (pretty_printer *pp) const final override; }; } // namespace ana @@ -442,6 +458,7 @@ public: /* region vfuncs. */ void dump_to_pp (pretty_printer *pp, bool simple) const final override; + void print_dump_widget_label (pretty_printer *pp) const final override; enum region_kind get_kind () const final override { return RK_CODE; } }; @@ -472,6 +489,8 @@ public: /* region vfuncs. */ void dump_to_pp (pretty_printer *pp, bool simple) const final override; + void print_dump_widget_label (pretty_printer *pp) const final override; + enum region_kind get_kind () const final override { return RK_FUNCTION; } const function_region * dyn_cast_function_region () const final override{ return this; } @@ -508,6 +527,7 @@ public: /* region vfuncs. */ void dump_to_pp (pretty_printer *pp, bool simple) const final override; + void print_dump_widget_label (pretty_printer *pp) const final override; enum region_kind get_kind () const final override { return RK_LABEL; } tree get_label () const { return m_label; } @@ -539,6 +559,7 @@ public: {} void dump_to_pp (pretty_printer *pp, bool simple) const final override; + void print_dump_widget_label (pretty_printer *pp) const final override; enum region_kind get_kind () const final override { return RK_STACK; } }; @@ -567,6 +588,7 @@ public: enum region_kind get_kind () const final override { return RK_HEAP; } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + void print_dump_widget_label (pretty_printer *pp) const final override; }; } // namespace ana @@ -593,6 +615,7 @@ public: enum region_kind get_kind () const final override { return RK_THREAD_LOCAL; } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + void print_dump_widget_label (pretty_printer *pp) const final override; }; } // namespace ana @@ -618,6 +641,7 @@ public: enum region_kind get_kind () const final override { return RK_ROOT; } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + void print_dump_widget_label (pretty_printer *pp) const final override; }; } // namespace ana @@ -680,6 +704,11 @@ public: enum region_kind get_kind () const final override { return RK_SYMBOLIC; } void accept (visitor *v) const final override; void dump_to_pp (pretty_printer *pp, bool simple) const final override; + void print_dump_widget_label (pretty_printer *pp) const final override; + void + add_dump_widget_children (text_art::tree_widget &w, + const text_art::dump_widget_info &dwi) + const final override; const svalue *get_pointer () const { return m_sval_ptr; } @@ -724,6 +753,9 @@ public: void dump_to_pp (pretty_printer *pp, bool simple) const final override; + void + print_dump_widget_label (pretty_printer *pp) const final override; + bool tracked_p () const final override { return m_tracked; } tree get_decl () const { return m_decl; } @@ -808,6 +840,8 @@ public: enum region_kind get_kind () const final override { return RK_FIELD; } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + void print_dump_widget_label (pretty_printer *pp) const final override; + const field_region * dyn_cast_field_region () const final override { return this; } @@ -896,6 +930,13 @@ public: void dump_to_pp (pretty_printer *pp, bool simple) const final override; + void + print_dump_widget_label (pretty_printer *pp) const final override; + void + add_dump_widget_children (text_art::tree_widget &, + const text_art::dump_widget_info &dwi) + const final override; + const svalue *get_index () const { return m_index; } virtual bool @@ -983,6 +1024,13 @@ public: void dump_to_pp (pretty_printer *pp, bool simple) const final override; + void + print_dump_widget_label (pretty_printer *pp) const final override; + void + add_dump_widget_children (text_art::tree_widget &, + const text_art::dump_widget_info &dwi) + const final override; + const svalue *get_byte_offset () const { return m_byte_offset; } const svalue *get_bit_offset (region_model_manager *mgr) const; @@ -1073,6 +1121,12 @@ public: void accept (visitor *v) const final override; void dump_to_pp (pretty_printer *pp, bool simple) const final override; + void + print_dump_widget_label (pretty_printer *pp) const final override; + void + add_dump_widget_children (text_art::tree_widget &, + const text_art::dump_widget_info &dwi) + const final override; bool get_byte_size (byte_size_t *out) const final override; bool get_bit_size (bit_size_t *out) const final override; @@ -1162,6 +1216,12 @@ public: dyn_cast_cast_region () const final override { return this; } void accept (visitor *v) const final override; void dump_to_pp (pretty_printer *pp, bool simple) const final override; + void + print_dump_widget_label (pretty_printer *pp) const final override; + void + add_dump_widget_children (text_art::tree_widget &, + const text_art::dump_widget_info &dwi) + const final override; bool get_relative_concrete_offset (bit_offset_t *out) const final override; @@ -1203,6 +1263,7 @@ public: get_kind () const final override { return RK_HEAP_ALLOCATED; } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + void print_dump_widget_label (pretty_printer *pp) const final override; }; /* An untyped region dynamically allocated on the stack via "alloca". */ @@ -1217,6 +1278,7 @@ public: enum region_kind get_kind () const final override { return RK_ALLOCA; } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + void print_dump_widget_label (pretty_printer *pp) const final override; }; /* A region for a STRING_CST. */ @@ -1235,6 +1297,7 @@ public: enum region_kind get_kind () const final override { return RK_STRING; } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + void print_dump_widget_label (pretty_printer *pp) const final override; /* We assume string literals are immutable, so we don't track them in the store. */ @@ -1314,6 +1377,7 @@ public: enum region_kind get_kind () const final override { return RK_BIT_RANGE; } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + void print_dump_widget_label (pretty_printer *pp) const final override; const bit_range &get_bits () const { return m_bits; } @@ -1402,6 +1466,7 @@ public: enum region_kind get_kind () const final override { return RK_VAR_ARG; } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + void print_dump_widget_label (pretty_printer *pp) const final override; const frame_region *get_frame_region () const; unsigned get_index () const { return m_idx; } @@ -1440,6 +1505,7 @@ public: enum region_kind get_kind () const final override { return RK_ERRNO; } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + void print_dump_widget_label (pretty_printer *pp) const final override; }; } // namespace ana @@ -1473,6 +1539,7 @@ public: enum region_kind get_kind () const final override { return RK_PRIVATE; } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + void print_dump_widget_label (pretty_printer *pp) const final override; private: const char *m_desc; @@ -1502,6 +1569,7 @@ public: enum region_kind get_kind () const final override { return RK_UNKNOWN; } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + void print_dump_widget_label (pretty_printer *pp) const final override; }; } // namespace ana diff --git a/gcc/analyzer/sm-fd.cc b/gcc/analyzer/sm-fd.cc index 6baac2a0c693..ded20576fd10 100644 --- a/gcc/analyzer/sm-fd.cc +++ b/gcc/analyzer/sm-fd.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "make-unique.h" diff --git a/gcc/analyzer/sm-file.cc b/gcc/analyzer/sm-file.cc index f85b40049692..e7af46a6eb89 100644 --- a/gcc/analyzer/sm-file.cc +++ b/gcc/analyzer/sm-file.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "make-unique.h" diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc index fc6718ad74ba..8bdcb4bc33cd 100644 --- a/gcc/analyzer/sm-malloc.cc +++ b/gcc/analyzer/sm-malloc.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "make-unique.h" diff --git a/gcc/analyzer/sm-pattern-test.cc b/gcc/analyzer/sm-pattern-test.cc index 866e07b43fb8..674d95859b2c 100644 --- a/gcc/analyzer/sm-pattern-test.cc +++ b/gcc/analyzer/sm-pattern-test.cc @@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "make-unique.h" diff --git a/gcc/analyzer/sm-signal.cc b/gcc/analyzer/sm-signal.cc index 84603c5b77b7..6a94b5f5c5ff 100644 --- a/gcc/analyzer/sm-signal.cc +++ b/gcc/analyzer/sm-signal.cc @@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "make-unique.h" diff --git a/gcc/analyzer/sm-taint.cc b/gcc/analyzer/sm-taint.cc index a9c6d4db43f4..bd85f0d56113 100644 --- a/gcc/analyzer/sm-taint.cc +++ b/gcc/analyzer/sm-taint.cc @@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "make-unique.h" diff --git a/gcc/analyzer/sm.cc b/gcc/analyzer/sm.cc index fcae035adea3..f23a94b62438 100644 --- a/gcc/analyzer/sm.cc +++ b/gcc/analyzer/sm.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" diff --git a/gcc/analyzer/state-purge.cc b/gcc/analyzer/state-purge.cc index 324b548f75b1..7f5a40ae4add 100644 --- a/gcc/analyzer/state-purge.cc +++ b/gcc/analyzer/state-purge.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" diff --git a/gcc/analyzer/store.cc b/gcc/analyzer/store.cc index a36de13c1743..d14cfa329b8d 100644 --- a/gcc/analyzer/store.cc +++ b/gcc/analyzer/store.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" @@ -54,6 +55,7 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/call-summary.h" #include "analyzer/analyzer-selftests.h" #include "stor-layout.h" +#include "text-art/tree-widget.h" #if ENABLE_ANALYZER @@ -810,6 +812,56 @@ binding_map::to_json () const return map_obj; } +/* Add a child to PARENT_WIDGET expressing a binding between + KEY and SVAL. */ + +static void +add_binding_to_tree_widget (text_art::tree_widget &parent_widget, + const text_art::dump_widget_info &dwi, + const binding_key *key, + const svalue *sval) +{ + pretty_printer the_pp; + pretty_printer * const pp = &the_pp; + pp_format_decoder (pp) = default_tree_printer; + pp_show_color (pp) = true; + const bool simple = true; + + key->dump_to_pp (pp, simple); + pp_string (pp, ": "); + if (tree t = sval->get_type ()) + dump_quoted_tree (pp, t); + pp_string (pp, " {"); + sval->dump_to_pp (pp, simple); + pp_string (pp, "}"); + + parent_widget.add_child (text_art::tree_widget::make (dwi, pp)); +} + +void +binding_map::add_to_tree_widget (text_art::tree_widget &parent_widget, + const text_art::dump_widget_info &dwi) const +{ + auto_vec <const binding_key *> binding_keys; + for (map_t::iterator iter = m_map.begin (); + iter != m_map.end (); ++iter) + { + const binding_key *key = (*iter).first; + binding_keys.safe_push (key); + } + binding_keys.qsort (binding_key::cmp_ptrs); + + const binding_key *key; + unsigned i; + FOR_EACH_VEC_ELT (binding_keys, i, key) + { + const svalue *sval = *const_cast <map_t &> (m_map).get (key); + add_binding_to_tree_widget (parent_widget, dwi, + key, sval); + } +} + + /* Comparator for imposing an order on binding_maps. */ int @@ -1399,6 +1451,48 @@ binding_cluster::to_json () const return cluster_obj; } +std::unique_ptr<text_art::widget> +binding_cluster::make_dump_widget (const text_art::dump_widget_info &dwi, + store_manager *mgr) const +{ + pretty_printer the_pp; + pretty_printer * const pp = &the_pp; + pp_format_decoder (pp) = default_tree_printer; + pp_show_color (pp) = true; + const bool simple = true; + + m_base_region->dump_to_pp (pp, simple); + pp_string (pp, ": "); + + if (const svalue *sval = maybe_get_simple_value (mgr)) + { + /* Special-case to simplify dumps for the common case where + we just have one value directly bound to the whole of a + region. */ + sval->dump_to_pp (pp, simple); + if (escaped_p ()) + pp_string (pp, " (ESCAPED)"); + if (touched_p ()) + pp_string (pp, " (TOUCHED)"); + + return text_art::tree_widget::make (dwi, pp); + } + else + { + if (escaped_p ()) + pp_string (pp, " (ESCAPED)"); + if (touched_p ()) + pp_string (pp, " (TOUCHED)"); + + std::unique_ptr<text_art::tree_widget> cluster_widget + (text_art::tree_widget::make (dwi, pp)); + + m_map.add_to_tree_widget (*cluster_widget, dwi); + + return cluster_widget; + } +} + /* Add a binding of SVAL of kind KIND to REG, unpacking SVAL if it is a compound_sval. */ @@ -2613,6 +2707,68 @@ store::to_json () const return store_obj; } +std::unique_ptr<text_art::widget> +store::make_dump_widget (const text_art::dump_widget_info &dwi, + store_manager *mgr) const +{ + std::unique_ptr<text_art::tree_widget> store_widget + (text_art::tree_widget::make (dwi, "Store")); + + store_widget->add_child + (text_art::tree_widget::from_fmt (dwi, nullptr, + "m_called_unknown_fn: %s", + m_called_unknown_fn ? "true" : "false")); + + /* Sort into some deterministic order. */ + auto_vec<const region *> base_regions; + for (cluster_map_t::iterator iter = m_cluster_map.begin (); + iter != m_cluster_map.end (); ++iter) + { + const region *base_reg = (*iter).first; + base_regions.safe_push (base_reg); + } + base_regions.qsort (region::cmp_ptr_ptr); + + /* Gather clusters, organize by parent region, so that we can group + together locals, globals, etc. */ + auto_vec<const region *> parent_regions; + get_sorted_parent_regions (&parent_regions, base_regions); + + const region *parent_reg; + unsigned i; + FOR_EACH_VEC_ELT (parent_regions, i, parent_reg) + { + gcc_assert (parent_reg); + + pretty_printer the_pp; + pretty_printer * const pp = &the_pp; + pp_format_decoder (pp) = default_tree_printer; + pp_show_color (pp) = true; + const bool simple = true; + + parent_reg->dump_to_pp (pp, simple); + + std::unique_ptr<text_art::tree_widget> parent_reg_widget + (text_art::tree_widget::make (dwi, pp)); + + const region *base_reg; + unsigned j; + FOR_EACH_VEC_ELT (base_regions, j, base_reg) + { + /* This is O(N * M), but N ought to be small. */ + if (base_reg->get_parent_region () != parent_reg) + continue; + binding_cluster *cluster + = *const_cast<cluster_map_t &> (m_cluster_map).get (base_reg); + parent_reg_widget->add_child + (cluster->make_dump_widget (dwi, mgr)); + } + store_widget->add_child (std::move (parent_reg_widget)); + } + + return store_widget; +} + /* Get any svalue bound to REG, or NULL. */ const svalue * diff --git a/gcc/analyzer/store.h b/gcc/analyzer/store.h index 2b603654946d..9be9df723794 100644 --- a/gcc/analyzer/store.h +++ b/gcc/analyzer/store.h @@ -21,6 +21,8 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_ANALYZER_STORE_H #define GCC_ANALYZER_STORE_H +#include "text-art/tree-widget.h" + /* Implementation of the region-based ternary model described in: "A Memory Model for Static Analysis of C Programs" (Zhongxing Xu, Ted Kremenek, and Jian Zhang) @@ -546,6 +548,9 @@ public: json::object *to_json () const; + void add_to_tree_widget (text_art::tree_widget &parent_widget, + const text_art::dump_widget_info &dwi) const; + bool apply_ctor_to_region (const region *parent_reg, tree ctor, region_model_manager *mgr); @@ -612,6 +617,10 @@ public: json::object *to_json () const; + std::unique_ptr<text_art::widget> + make_dump_widget (const text_art::dump_widget_info &dwi, + store_manager *mgr) const; + void bind (store_manager *mgr, const region *, const svalue *); void clobber_region (store_manager *mgr, const region *reg); @@ -750,6 +759,10 @@ public: json::object *to_json () const; + std::unique_ptr<text_art::widget> + make_dump_widget (const text_art::dump_widget_info &dwi, + store_manager *mgr) const; + const svalue *get_any_binding (store_manager *mgr, const region *reg) const; bool called_unknown_fn_p () const { return m_called_unknown_fn; } diff --git a/gcc/analyzer/svalue.cc b/gcc/analyzer/svalue.cc index e64f62d266e1..f1fd21e4cda9 100644 --- a/gcc/analyzer/svalue.cc +++ b/gcc/analyzer/svalue.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" @@ -49,6 +50,8 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/region-model.h" #include "diagnostic.h" #include "tree-diagnostic.h" +#include "make-unique.h" +#include "text-art/dump.h" #if ENABLE_ANALYZER @@ -60,6 +63,24 @@ static int cmp_csts_and_types (const_tree cst1, const_tree cst2); /* class svalue. */ +/* Dump a tree-like representation of this svalue and its constituent symbols + to stderr, using global_dc's colorization and theming options. + + For example: + . (gdb) call index_sval->dump() + . (27): ‘int’: initial_svalue + . ╰─ m_reg: (26): ‘int’: decl_region(‘x_10(D)’) + . ╰─ parent: (9): frame_region(‘test_bitmask_2’, index: 0, depth: 1) + . ╰─ parent: (1): stack region + . ╰─ parent: (0): root region + */ + +DEBUG_FUNCTION void +svalue::dump () const +{ + text_art::dump (*this); +} + /* Dump a representation of this svalue to stderr. */ DEBUG_FUNCTION void @@ -203,6 +224,37 @@ svalue::maybe_print_for_user (pretty_printer *pp, return false; } +/* Use DWI to create a text_art::widget describing this svalue in + a tree-like form, using PREFIX as a prefix (e.g. for field names). + We do this via two vfuncs: + (a) print_dump_widget_label, to populate the text of a tree_widget, and + (b) add_dump_widget_children, to add children to the tree_widget. */ + +std::unique_ptr<text_art::widget> +svalue::make_dump_widget (const text_art::dump_widget_info &dwi, + const char *prefix) const +{ + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + pp_show_color (&pp) = true; + + if (prefix) + pp_printf (&pp, "%s: ", prefix); + + pp_printf (&pp, "(%i): ", get_id ()); + if (get_type ()) + pp_printf (&pp, "%qT: ", get_type ()); + + print_dump_widget_label (&pp); + + std::unique_ptr<text_art::tree_widget> w + (text_art::tree_widget::make (dwi, &pp)); + + add_dump_widget_children (*w, dwi); + + return w; +} + /* If this svalue is a constant_svalue, return the underlying tree constant. Otherwise return NULL_TREE. */ @@ -842,6 +894,26 @@ region_svalue::dump_to_pp (pretty_printer *pp, bool simple) const } } +/* Implementation of svalue::print_dump_widget_label vfunc for + region_svalue. */ + +void +region_svalue::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "region_svalue: %qs", "&"); +} + +/* Implementation of svalue::add_dump_widget_children vfunc for + region_svalue. */ + +void +region_svalue:: +add_dump_widget_children (text_art::tree_widget &w, + const text_art::dump_widget_info &dwi) const +{ + w.add_child (m_reg->make_dump_widget (dwi)); +} + /* Implementation of svalue::accept vfunc for region_svalue. */ void @@ -942,6 +1014,26 @@ constant_svalue::dump_to_pp (pretty_printer *pp, bool simple) const } } +/* Implementation of svalue::print_dump_widget_label vfunc for + constant_svalue. */ + +void +constant_svalue::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "constant_svalue (%qE)", m_cst_expr); +} + +/* Implementation of svalue::add_dump_widget_children vfunc for + constant_svalue. */ + +void +constant_svalue:: +add_dump_widget_children (text_art::tree_widget &, + const text_art::dump_widget_info &) const +{ + /* No children. */ +} + /* Implementation of svalue::accept vfunc for constant_svalue. */ void @@ -1085,6 +1177,26 @@ unknown_svalue::dump_to_pp (pretty_printer *pp, bool simple) const } } +/* Implementation of svalue::print_dump_widget_label vfunc for + unknown_svalue. */ + +void +unknown_svalue::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "unknown_svalue"); +} + +/* Implementation of svalue::add_dump_widget_children vfunc for + unknown_svalue. */ + +void +unknown_svalue:: +add_dump_widget_children (text_art::tree_widget &, + const text_art::dump_widget_info &) const +{ + /* No children. */ +} + /* Implementation of svalue::accept vfunc for unknown_svalue. */ void @@ -1146,6 +1258,26 @@ poisoned_svalue::dump_to_pp (pretty_printer *pp, bool simple) const } } +/* Implementation of svalue::print_dump_widget_label vfunc for + poisoned_svalue. */ + +void +poisoned_svalue::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "poisoned_svalue(%s)", poison_kind_to_str (m_kind)); +} + +/* Implementation of svalue::add_dump_widget_children vfunc for + poisoned_svalue. */ + +void +poisoned_svalue:: +add_dump_widget_children (text_art::tree_widget &, + const text_art::dump_widget_info &) const +{ + /* No children. */ +} + /* Implementation of svalue::accept vfunc for poisoned_svalue. */ void @@ -1195,6 +1327,26 @@ initial_svalue::dump_to_pp (pretty_printer *pp, bool simple) const } } +/* Implementation of svalue::print_dump_widget_label vfunc for + initial_svalue. */ + +void +initial_svalue::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "initial_svalue"); +} + +/* Implementation of svalue::add_dump_widget_children vfunc for + initial_svalue. */ + +void +initial_svalue:: +add_dump_widget_children (text_art::tree_widget &w, + const text_art::dump_widget_info &dwi) const +{ + w.add_child (m_reg->make_dump_widget (dwi, "m_reg")); +} + /* Implementation of svalue::accept vfunc for initial_svalue. */ void @@ -1287,6 +1439,28 @@ unaryop_svalue::dump_to_pp (pretty_printer *pp, bool simple) const } } +/* Implementation of svalue::print_dump_widget_label vfunc for + unaryop_svalue. */ + +void +unaryop_svalue::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, + "unaryop_svalue(%s)", + get_tree_code_name (m_op)); +} + +/* Implementation of svalue::add_dump_widget_children vfunc for + unaryop_svalue. */ + +void +unaryop_svalue:: +add_dump_widget_children (text_art::tree_widget &w, + const text_art::dump_widget_info &dwi) const +{ + w.add_child (m_arg->make_dump_widget (dwi)); +} + /* Implementation of svalue::accept vfunc for unaryop_svalue. */ void @@ -1389,6 +1563,30 @@ binop_svalue::dump_to_pp (pretty_printer *pp, bool simple) const } } +/* Implementation of svalue::print_dump_widget_label vfunc for + binop_svalue. */ + +void +binop_svalue::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, + "binop_svalue(%s: %qs)", + get_tree_code_name (m_op), + op_symbol_code (m_op)); +} + +/* Implementation of svalue::add_dump_widget_children vfunc for + binop_svalue. */ + +void +binop_svalue:: +add_dump_widget_children (text_art::tree_widget &w, + const text_art::dump_widget_info &dwi) const +{ + w.add_child (m_arg0->make_dump_widget (dwi)); + w.add_child (m_arg1->make_dump_widget (dwi)); +} + /* Implementation of svalue::accept vfunc for binop_svalue. */ void @@ -1449,6 +1647,27 @@ sub_svalue::dump_to_pp (pretty_printer *pp, bool simple) const } } +/* Implementation of svalue::print_dump_widget_label vfunc for + sub_svalue. */ + +void +sub_svalue::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "sub_svalue"); +} + +/* Implementation of svalue::add_dump_widget_children vfunc for + sub_svalue. */ + +void +sub_svalue:: +add_dump_widget_children (text_art::tree_widget &w, + const text_art::dump_widget_info &dwi) const +{ + w.add_child (m_parent_svalue->make_dump_widget (dwi, "m_parent_svalue")); + w.add_child (m_subregion->make_dump_widget (dwi, "m_subregion")); +} + /* Implementation of svalue::accept vfunc for sub_svalue. */ void @@ -1519,6 +1738,27 @@ repeated_svalue::dump_to_pp (pretty_printer *pp, bool simple) const } } +/* Implementation of svalue::print_dump_widget_label vfunc for + repeated_svalue. */ + +void +repeated_svalue::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "repeated_svalue"); +} + +/* Implementation of svalue::add_dump_widget_children vfunc for + repeated_svalue. */ + +void +repeated_svalue:: +add_dump_widget_children (text_art::tree_widget &w, + const text_art::dump_widget_info &dwi) const +{ + w.add_child (m_outer_size->make_dump_widget (dwi, "m_outer_size")); + w.add_child (m_inner_svalue->make_dump_widget (dwi, "m_inner_svalue")); +} + /* Implementation of svalue::accept vfunc for repeated_svalue. */ void @@ -1644,6 +1884,27 @@ bits_within_svalue::dump_to_pp (pretty_printer *pp, bool simple) const } } +/* Implementation of svalue::print_dump_widget_label vfunc for + bits_within_svalue. */ + +void +bits_within_svalue::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "bits_within_svalue: "); + m_bits.dump_to_pp (pp); +} + +/* Implementation of svalue::add_dump_widget_children vfunc for + bits_within_svalue. */ + +void +bits_within_svalue:: +add_dump_widget_children (text_art::tree_widget &w, + const text_art::dump_widget_info &dwi) const +{ + w.add_child (m_inner_svalue->make_dump_widget (dwi, "m_inner_svalue")); +} + /* Implementation of svalue::maybe_fold_bits_within vfunc for bits_within_svalue. */ @@ -1712,6 +1973,28 @@ widening_svalue::dump_to_pp (pretty_printer *pp, bool simple) const } } +/* Implementation of svalue::print_dump_widget_label vfunc for + widening_svalue. */ + +void +widening_svalue::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "widening_svalue at "); + m_point.print (pp, format (false)); +} + +/* Implementation of svalue::add_dump_widget_children vfunc for + widening_svalue. */ + +void +widening_svalue:: +add_dump_widget_children (text_art::tree_widget &w, + const text_art::dump_widget_info &dwi) const +{ + w.add_child (m_base_sval->make_dump_widget (dwi, "m_base_sval")); + w.add_child (m_iter_sval->make_dump_widget (dwi, "m_iter_sval")); +} + /* Implementation of svalue::accept vfunc for widening_svalue. */ void @@ -1850,6 +2133,26 @@ placeholder_svalue::dump_to_pp (pretty_printer *pp, bool simple) const pp_printf (pp, "placeholder_svalue (%qs)", m_name); } +/* Implementation of svalue::print_dump_widget_label vfunc for + placeholder_svalue. */ + +void +placeholder_svalue::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "placeholder_svalue: %qs", m_name); +} + +/* Implementation of svalue::add_dump_widget_children vfunc for + placeholder_svalue. */ + +void +placeholder_svalue:: +add_dump_widget_children (text_art::tree_widget &, + const text_art::dump_widget_info &) const +{ + /* No children. */ +} + /* Implementation of svalue::accept vfunc for placeholder_svalue. */ void @@ -1879,6 +2182,26 @@ unmergeable_svalue::dump_to_pp (pretty_printer *pp, bool simple) const } } +/* Implementation of svalue::print_dump_widget_label vfunc for + unmergeable_svalue. */ + +void +unmergeable_svalue::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "unmergeable_svalue"); +} + +/* Implementation of svalue::add_dump_widget_children vfunc for + unmergeable_svalue. */ + +void +unmergeable_svalue:: +add_dump_widget_children (text_art::tree_widget &w, + const text_art::dump_widget_info &dwi) const +{ + w.add_child (m_arg->make_dump_widget (dwi)); +} + /* Implementation of svalue::accept vfunc for unmergeable_svalue. */ void @@ -1950,6 +2273,26 @@ compound_svalue::dump_to_pp (pretty_printer *pp, bool simple) const } } +/* Implementation of svalue::print_dump_widget_label vfunc for + compound_svalue. */ + +void +compound_svalue::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "compound_svalue"); +} + +/* Implementation of svalue::add_dump_widget_children vfunc for + compound_svalue. */ + +void +compound_svalue:: +add_dump_widget_children (text_art::tree_widget &w, + const text_art::dump_widget_info &dwi) const +{ + m_map.add_to_tree_widget (w, dwi); +} + /* Implementation of svalue::accept vfunc for compound_svalue. */ void @@ -2081,6 +2424,30 @@ conjured_svalue::dump_to_pp (pretty_printer *pp, bool simple) const } } +/* Implementation of svalue::print_dump_widget_label vfunc for + conjured_svalue. */ + +void +conjured_svalue::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "conjured_svalue ("); + pp_gimple_stmt_1 (pp, m_stmt, 0, (dump_flags_t)0); + if (m_idx != 0) + pp_printf (pp, ", %i", m_idx); + pp_character (pp, ')'); +} + +/* Implementation of svalue::add_dump_widget_children vfunc for + conjured_svalue. */ + +void +conjured_svalue:: +add_dump_widget_children (text_art::tree_widget &w, + const text_art::dump_widget_info &dwi) const +{ + w.add_child (m_id_reg->make_dump_widget (dwi)); +} + /* Implementation of svalue::accept vfunc for conjured_svalue. */ void @@ -2136,6 +2503,34 @@ asm_output_svalue::dump_to_pp (pretty_printer *pp, bool simple) const } } +/* Implementation of svalue::print_dump_widget_label vfunc for + asm_output_svalue. */ + +void +asm_output_svalue::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "asm_output_svalue(%qs, %%%i)", + get_asm_string (), + get_output_idx ()); +} + +/* Implementation of svalue::add_dump_widget_children vfunc for + asm_output_svalue. */ + +void +asm_output_svalue:: +add_dump_widget_children (text_art::tree_widget &w, + const text_art::dump_widget_info &dwi) const +{ + for (unsigned i = 0; i < m_num_inputs; i++) + { + pretty_printer pp; + pp_printf (&pp, "arg %i", i); + w.add_child (m_input_arr[i]->make_dump_widget (dwi, + pp_formatted_text (&pp))); + } +} + /* Subroutine of asm_output_svalue::dump_to_pp. */ void @@ -2198,6 +2593,32 @@ const_fn_result_svalue::dump_to_pp (pretty_printer *pp, bool simple) const } } +/* Implementation of svalue::print_dump_widget_label vfunc for + const_fn_result_svalue. */ + +void +const_fn_result_svalue::print_dump_widget_label (pretty_printer *pp) const +{ + pp_printf (pp, "const_fn_result_svalue: %qD", m_fndecl); +} + +/* Implementation of svalue::add_dump_widget_children vfunc for + const_fn_result_svalue. */ + +void +const_fn_result_svalue:: +add_dump_widget_children (text_art::tree_widget &w, + const text_art::dump_widget_info &dwi) const +{ + for (unsigned i = 0; i < m_num_inputs; i++) + { + pretty_printer pp; + pp_printf (&pp, "arg %i", i); + w.add_child (m_input_arr[i]->make_dump_widget (dwi, + pp_formatted_text (&pp))); + } +} + /* Subroutine of const_fn_result_svalue::dump_to_pp. */ void diff --git a/gcc/analyzer/svalue.h b/gcc/analyzer/svalue.h index 3a2b0319a195..e5503a541779 100644 --- a/gcc/analyzer/svalue.h +++ b/gcc/analyzer/svalue.h @@ -24,8 +24,10 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/symbol.h" #include "analyzer/store.h" #include "analyzer/program-point.h" +#include "text-art/widget.h" using namespace ana; +using text_art::dump_widget_info; namespace ana { @@ -99,11 +101,16 @@ public: pretty_printer *pp) const; virtual void dump_to_pp (pretty_printer *pp, bool simple) const = 0; - void dump (bool simple=true) const; + void dump () const; + void dump (bool simple) const; label_text get_desc (bool simple=true) const; json::value *to_json () const; + std::unique_ptr<text_art::widget> + make_dump_widget (const dump_widget_info &dwi, + const char *prefix = nullptr) const; + virtual const region_svalue * dyn_cast_region_svalue () const { return NULL; } virtual const constant_svalue * @@ -186,7 +193,15 @@ public: : symbol (c, id), m_type (type) {} + void print_svalue_node_label (pretty_printer *pp) const; + private: + virtual void + print_dump_widget_label (pretty_printer *pp) const = 0; + virtual void + add_dump_widget_children (text_art::tree_widget &, + const dump_widget_info &dwi) const = 0; + tree m_type; }; @@ -237,6 +252,13 @@ public: dyn_cast_region_svalue () const final override { return this; } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + + void + print_dump_widget_label (pretty_printer *pp) const final override; + void + add_dump_widget_children (text_art::tree_widget &w, + const dump_widget_info &dwi) const final override; + void accept (visitor *v) const final override; bool implicitly_live_p (const svalue_set *, const region_model *) const final override; @@ -319,6 +341,13 @@ public: dyn_cast_constant_svalue () const final override { return this; } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + + void + print_dump_widget_label (pretty_printer *pp) const final override; + void + add_dump_widget_children (text_art::tree_widget &w, + const dump_widget_info &dwi) const final override; + void accept (visitor *v) const final override; bool implicitly_live_p (const svalue_set *, const region_model *) const final override; @@ -372,6 +401,13 @@ public: enum svalue_kind get_kind () const final override { return SK_UNKNOWN; } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + + void + print_dump_widget_label (pretty_printer *pp) const final override; + void + add_dump_widget_children (text_art::tree_widget &w, + const dump_widget_info &dwi) const final override; + void accept (visitor *v) const final override; const svalue * @@ -445,6 +481,13 @@ public: dyn_cast_poisoned_svalue () const final override { return this; } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + + void + print_dump_widget_label (pretty_printer *pp) const final override; + void + add_dump_widget_children (text_art::tree_widget &w, + const dump_widget_info &dwi) const final override; + void accept (visitor *v) const final override; const svalue * @@ -555,6 +598,13 @@ public: dyn_cast_setjmp_svalue () const final override { return this; } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + + void + print_dump_widget_label (pretty_printer *pp) const final override; + void + add_dump_widget_children (text_art::tree_widget &w, + const dump_widget_info &dwi) const final override; + void accept (visitor *v) const final override; int get_enode_index () const; @@ -605,6 +655,13 @@ public: dyn_cast_initial_svalue () const final override { return this; } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + + void + print_dump_widget_label (pretty_printer *pp) const final override; + void + add_dump_widget_children (text_art::tree_widget &w, + const dump_widget_info &dwi) const final override; + void accept (visitor *v) const final override; bool implicitly_live_p (const svalue_set *, const region_model *) const final override; @@ -680,6 +737,13 @@ public: dyn_cast_unaryop_svalue () const final override { return this; } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + + void + print_dump_widget_label (pretty_printer *pp) const final override; + void + add_dump_widget_children (text_art::tree_widget &w, + const dump_widget_info &dwi) const final override; + void accept (visitor *v) const final override; bool implicitly_live_p (const svalue_set *, const region_model *) const final override; @@ -777,6 +841,13 @@ public: } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + + void + print_dump_widget_label (pretty_printer *pp) const final override; + void + add_dump_widget_children (text_art::tree_widget &w, + const dump_widget_info &dwi) const final override; + void accept (visitor *v) const final override; bool implicitly_live_p (const svalue_set *, const region_model *) const final override; @@ -858,6 +929,13 @@ public: } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + + void + print_dump_widget_label (pretty_printer *pp) const final override; + void + add_dump_widget_children (text_art::tree_widget &w, + const dump_widget_info &dwi) const final override; + void accept (visitor *v) const final override; bool implicitly_live_p (const svalue_set *, const region_model *) const final override; @@ -941,6 +1019,13 @@ public: } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + + void + print_dump_widget_label (pretty_printer *pp) const final override; + void + add_dump_widget_children (text_art::tree_widget &w, + const dump_widget_info &dwi) const final override; + void accept (visitor *v) const final override; const svalue *get_outer_size () const { return m_outer_size; } @@ -1030,6 +1115,13 @@ public: } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + + void + print_dump_widget_label (pretty_printer *pp) const final override; + void + add_dump_widget_children (text_art::tree_widget &w, + const dump_widget_info &dwi) const final override; + void accept (visitor *v) const final override; bool implicitly_live_p (const svalue_set *, const region_model *) const final override; @@ -1089,6 +1181,13 @@ public: dyn_cast_unmergeable_svalue () const final override { return this; } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + + void + print_dump_widget_label (pretty_printer *pp) const final override; + void + add_dump_widget_children (text_art::tree_widget &w, + const dump_widget_info &dwi) const final override; + void accept (visitor *v) const final override; bool implicitly_live_p (const svalue_set *, const region_model *) const final override; @@ -1127,6 +1226,13 @@ public: enum svalue_kind get_kind () const final override { return SK_PLACEHOLDER; } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + + void + print_dump_widget_label (pretty_printer *pp) const final override; + void + add_dump_widget_children (text_art::tree_widget &w, + const dump_widget_info &dwi) const final override; + void accept (visitor *v) const final override; const char *get_name () const { return m_name; } @@ -1223,6 +1329,13 @@ public: } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + + void + print_dump_widget_label (pretty_printer *pp) const final override; + void + add_dump_widget_children (text_art::tree_widget &w, + const dump_widget_info &dwi) const final override; + void accept (visitor *v) const final override; const function_point &get_point () const { return m_point; } @@ -1318,6 +1431,13 @@ public: } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + + void + print_dump_widget_label (pretty_printer *pp) const final override; + void + add_dump_widget_children (text_art::tree_widget &w, + const dump_widget_info &dwi) const final override; + void accept (visitor *v) const final override; const binding_map &get_map () const { return m_map; } @@ -1455,6 +1575,13 @@ public: } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + + void + print_dump_widget_label (pretty_printer *pp) const final override; + void + add_dump_widget_children (text_art::tree_widget &w, + const dump_widget_info &dwi) const final override; + void accept (visitor *v) const final override; const gimple *get_stmt () const { return m_stmt; } @@ -1580,6 +1707,13 @@ public: } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + + void + print_dump_widget_label (pretty_printer *pp) const final override; + void + add_dump_widget_children (text_art::tree_widget &w, + const dump_widget_info &dwi) const final override; + void accept (visitor *v) const final override; const char *get_asm_string () const { return m_asm_string; } @@ -1713,6 +1847,13 @@ public: } void dump_to_pp (pretty_printer *pp, bool simple) const final override; + + void + print_dump_widget_label (pretty_printer *pp) const final override; + void + add_dump_widget_children (text_art::tree_widget &w, + const dump_widget_info &dwi) const final override; + void accept (visitor *v) const final override; tree get_fndecl () const { return m_fndecl; } diff --git a/gcc/analyzer/trimmed-graph.cc b/gcc/analyzer/trimmed-graph.cc index b24b334c2ee3..a03cae5862b1 100644 --- a/gcc/analyzer/trimmed-graph.cc +++ b/gcc/analyzer/trimmed-graph.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" diff --git a/gcc/analyzer/varargs.cc b/gcc/analyzer/varargs.cc index 3348121a0ef8..684e1db20662 100644 --- a/gcc/analyzer/varargs.cc +++ b/gcc/analyzer/varargs.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "make-unique.h" diff --git a/gcc/doc/analyzer.texi b/gcc/doc/analyzer.texi index b53096e7b7d9..14034737f83c 100644 --- a/gcc/doc/analyzer.texi +++ b/gcc/doc/analyzer.texi @@ -259,56 +259,175 @@ memory, with a @code{store} recording a binding between @code{region} instances, to @code{svalue} instances. The bindings are organized into clusters, where regions accessible via well-defined pointer arithmetic are in the same cluster. The representation is graph-like because values -can be pointers to regions. It also stores a constraint_manager, +can be pointers to regions. It also stores a @code{constraint_manager}, capturing relationships between the values. Because each node in the @code{exploded_graph} has a @code{region_model}, and each of the latter is graph-like, the @code{exploded_graph} is in some ways a graph of graphs. -Here's an example of printing a @code{program_state}, showing the -@code{region_model} within it, along with state for the @code{malloc} -state machine. +There are several ``dump'' functions for use when debugging the analyzer. + +Consider this example C code: + +@smallexample +void * +calls_malloc (size_t n) +@{ + void *result = malloc (1024); + return result; /* HERE */ +@} + +void test (size_t n) +@{ + void *ptr = calls_malloc (n * 4); + /* etc. */ +@} +@end smallexample + +and the state at the point @code{/* HERE */} for the interprocedural +analysis case where @code{calls_malloc} returns back to @code{test}. + +Here's an example of printing a @code{program_state} at @code{/* HERE */}, +showing the @code{region_model} within it, along with state for the +@code{malloc} state machine. @smallexample -(gdb) call debug (*this) +(gdb) break region_model::on_return +[..snip...] +(gdb) run +[..snip...] +(gdb) up +[..snip...] +(gdb) call state->dump() +State +├─ Region Model +│ ├─ Current Frame: frame: ‘calls_malloc’@@2 +│ ├─ Store +│ │ ├─ m_called_unknown_fn: false +│ │ ├─ frame: ‘test’@@1 +│ │ │ ╰─ _1: (INIT_VAL(n_2(D))*(size_t)4) +│ │ ╰─ frame: ‘calls_malloc’@@2 +│ │ ├─ result_4: &HEAP_ALLOCATED_REGION(27) +│ │ ╰─ _5: &HEAP_ALLOCATED_REGION(27) +│ ╰─ Dynamic Extents +│ ╰─ HEAP_ALLOCATED_REGION(27): (INIT_VAL(n_2(D))*(size_t)4) +╰─ ‘malloc’ state machine + ╰─ 0x468cb40: &HEAP_ALLOCATED_REGION(27): unchecked (@{free@}) (‘result_4’) +@end smallexample + +Within the store, there are bindings clusters for the SSA names for the +various local variables within frames for @code{test} and +@code{calls_malloc}. For example, + +@itemize @bullet +@item +within @code{test} the whole cluster for @code{_1} is bound +to a @code{binop_svalue} representing @code{n * 4}, and +@item +within @code{test} the whole cluster for @code{result_4} is bound to a +@code{region_svalue} pointing at @code{HEAP_ALLOCATED_REGION(12)}. +@end itemize + +Additionally, this latter pointer has the @code{unchecked} state for the +@code{malloc} state machine indicating it hasn't yet been checked against +@code{NULL} since the allocation call. + +We also see that the state has captured the size of the heap-allocated +region (``Dynamic Extents''). + +This visualization can also be seen within the output of +@option{-fdump-analyzer-exploded-nodes-2} and +@option{-fdump-analyzer-exploded-nodes-3}. + +As well as the above visualizations of states, there are tree-like +visualizations for instances of @code{svalue} and @code{region}, showing +their IDs and how they are constructed from simpler symbols: + +@smallexample +(gdb) break region_model::set_dynamic_extents +[..snip...] +(gdb) run +[..snip...] +(gdb) up +[..snip...] +(gdb) call size_in_bytes->dump() +(17): ‘long unsigned int’: binop_svalue(mult_expr: ‘*’) +├─ (15): ‘size_t’: initial_svalue +│ ╰─ m_reg: (12): ‘size_t’: decl_region(‘n_2(D)’) +│ ╰─ parent: (9): frame_region(‘test’, index: 0, depth: 1) +│ ╰─ parent: (1): stack region +│ ╰─ parent: (0): root region +╰─ (16): ‘size_t’: constant_svalue (‘4’) +@end smallexample + +i.e. that @code{size_in_bytes} is a @code{binop_svalue} expressing +the result of multiplying + +@itemize @bullet +@item +the initial value of the @code{PARM_DECL} @code{n_2(D)} for the +parameter @code{n} within the frame for @code{test} by +@item +the constant value @code{4}. +@end itemize + +The above visualizations rely on the @code{text_art::widget} framework, +which performs significant work to lay out the output, so there is also +an earlier, simpler, form of dumping available. For states there is: + +@smallexample +(gdb) call state->dump(eg.m_ext_state, true) rmodel: -stack depth: 1 +stack depth: 2 + frame (index 1): frame: ‘calls_malloc’@@2 frame (index 0): frame: ‘test’@@1 clusters within frame: ‘test’@@1 - cluster for: ptr_3: &HEAP_ALLOCATED_REGION(12) + cluster for: _1: (INIT_VAL(n_2(D))*(size_t)4) +clusters within frame: ‘calls_malloc’@@2 + cluster for: result_4: &HEAP_ALLOCATED_REGION(27) + cluster for: _5: &HEAP_ALLOCATED_REGION(27) m_called_unknown_fn: FALSE constraint_manager: equiv classes: constraints: +dynamic_extents: + HEAP_ALLOCATED_REGION(27): (INIT_VAL(n_2(D))*(size_t)4) malloc: - 0x2e89590: &HEAP_ALLOCATED_REGION(12): unchecked ('ptr_3') + 0x468cb40: &HEAP_ALLOCATED_REGION(27): unchecked (@{free@}) (‘result_4’) @end smallexample -This is the state at the point of returning from @code{calls_malloc} back -to @code{test} in the following: +or for @code{region_model} just: @smallexample -void * -calls_malloc (void) -@{ - void *result = malloc (1024); - return result; -@} - -void test (void) -@{ - void *ptr = calls_malloc (); - /* etc. */ -@} +(gdb) call state->m_region_model->debug() +stack depth: 2 + frame (index 1): frame: ‘calls_malloc’@@2 + frame (index 0): frame: ‘test’@@1 +clusters within frame: ‘test’@@1 + cluster for: _1: (INIT_VAL(n_2(D))*(size_t)4) +clusters within frame: ‘calls_malloc’@@2 + cluster for: result_4: &HEAP_ALLOCATED_REGION(27) + cluster for: _5: &HEAP_ALLOCATED_REGION(27) +m_called_unknown_fn: FALSE +constraint_manager: + equiv classes: + constraints: +dynamic_extents: + HEAP_ALLOCATED_REGION(27): (INIT_VAL(n_2(D))*(size_t)4) @end smallexample -Within the store, there is the cluster for @code{ptr_3} within the frame -for @code{test}, where the whole cluster is bound to a pointer value, -pointing at @code{HEAP_ALLOCATED_REGION(12)}. Additionally, this pointer -has the @code{unchecked} state for the @code{malloc} state machine -indicating it hasn't yet been checked against NULL since the allocation -call. +and for instances of @code{svalue} and @code{region} there is this +older dump implementation, which takes a @code{bool simple} flag +controlling the verbosity of the dump: + +@smallexample +(gdb) call size_in_bytes->dump(true) +(INIT_VAL(n_2(D))*(size_t)4) + +(gdb) call size_in_bytes->dump(false) +binop_svalue (mult_expr, initial_svalue(‘size_t’, decl_region(frame_region(‘test’, index: 0, depth: 1), ‘size_t’, ‘n_2(D)’)), constant_svalue(‘size_t’, 4)) +@end smallexample @subsection Analyzer Paths diff --git a/gcc/testsuite/gcc.dg/plugin/analyzer_cpython_plugin.c b/gcc/testsuite/gcc.dg/plugin/analyzer_cpython_plugin.c index b5814dd709eb..1fc43007cf1f 100644 --- a/gcc/testsuite/gcc.dg/plugin/analyzer_cpython_plugin.c +++ b/gcc/testsuite/gcc.dg/plugin/analyzer_cpython_plugin.c @@ -2,6 +2,7 @@ /* { dg-options "-g" } */ #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "gcc-plugin.h" #include "config.h" #include "system.h" diff --git a/gcc/testsuite/gcc.dg/plugin/analyzer_gil_plugin.c b/gcc/testsuite/gcc.dg/plugin/analyzer_gil_plugin.c index 6ea6c03e1d52..57135fc59043 100644 --- a/gcc/testsuite/gcc.dg/plugin/analyzer_gil_plugin.c +++ b/gcc/testsuite/gcc.dg/plugin/analyzer_gil_plugin.c @@ -5,6 +5,7 @@ /* { dg-options "-g" } */ #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "gcc-plugin.h" #include "config.h" #include "system.h" diff --git a/gcc/testsuite/gcc.dg/plugin/analyzer_kernel_plugin.c b/gcc/testsuite/gcc.dg/plugin/analyzer_kernel_plugin.c index 5a32f8cc620a..7f2158ec54d5 100644 --- a/gcc/testsuite/gcc.dg/plugin/analyzer_kernel_plugin.c +++ b/gcc/testsuite/gcc.dg/plugin/analyzer_kernel_plugin.c @@ -2,6 +2,7 @@ /* { dg-options "-g" } */ #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "gcc-plugin.h" #include "config.h" #include "system.h" diff --git a/gcc/testsuite/gcc.dg/plugin/analyzer_known_fns_plugin.c b/gcc/testsuite/gcc.dg/plugin/analyzer_known_fns_plugin.c index 806cb90ef56a..c0604070552a 100644 --- a/gcc/testsuite/gcc.dg/plugin/analyzer_known_fns_plugin.c +++ b/gcc/testsuite/gcc.dg/plugin/analyzer_known_fns_plugin.c @@ -2,6 +2,7 @@ /* { dg-options "-g" } */ #define INCLUDE_MEMORY +#define INCLUDE_VECTOR #include "gcc-plugin.h" #include "config.h" #include "system.h" diff --git a/gcc/text-art/dump-widget-info.h b/gcc/text-art/dump-widget-info.h new file mode 100644 index 000000000000..8676852aeeb8 --- /dev/null +++ b/gcc/text-art/dump-widget-info.h @@ -0,0 +1,53 @@ +/* Support for creating dump widgets. + Copyright (C) 2024 Free Software Foundation, Inc. + Contributed by David Malcolm <dmalc...@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_TEXT_ART_DUMP_WIDGET_INFO_H +#define GCC_TEXT_ART_DUMP_WIDGET_INFO_H + +namespace text_art { + +/* A bundle of state for use by make_dump_widget implementations. + The referenced objects are expected to outlive the widgets + themselves. */ + +struct dump_widget_info +{ + dump_widget_info (text_art::style_manager &sm, + const text_art::theme &theme, + text_art::style::id_t tree_style_id) + : m_sm (sm), + m_theme (theme), + m_tree_style_id (tree_style_id) + { + } + + text_art::style::id_t get_tree_style_id () const + { + return m_tree_style_id; + } + + text_art::style_manager &m_sm; + const text_art::theme &m_theme; + text_art::style::id_t m_tree_style_id; +}; + +} // namespace text_art + +#endif /* GCC_TEXT_ART_DUMP_WIDGET_INFO_H */ diff --git a/gcc/text-art/dump.h b/gcc/text-art/dump.h new file mode 100644 index 000000000000..e94f308f8cee --- /dev/null +++ b/gcc/text-art/dump.h @@ -0,0 +1,83 @@ +/* Templates for dumping objects. + Copyright (C) 2024 Free Software Foundation, Inc. + Contributed by David Malcolm <dmalc...@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_TEXT_ART_DUMP_H +#define GCC_TEXT_ART_DUMP_H + +#include "tree-diagnostic.h" +#include "text-art/canvas.h" +#include "text-art/widget.h" +#include "text-art/dump-widget-info.h" + +/* A family of templates for dumping objects via the text_art::widget + system. + Any type T that has a make_dump_widget member function ought to be + dumpable via these functions. */ + +namespace text_art { + +/* Dump OBJ to PP, using OBJ's make_dump_widget member function. */ + +template <typename T> +void dump_to_pp (const T &obj, text_art::theme *theme, pretty_printer *pp) +{ + if (!theme) + return; + + style_manager sm; + style tree_style (get_style_from_color_cap_name ("note")); + + style::id_t tree_style_id (sm.get_or_create_id (tree_style)); + + dump_widget_info dwi (sm, *theme, tree_style_id); + if (std::unique_ptr<widget> w = obj.make_dump_widget (dwi)) + { + text_art::canvas c (w->to_canvas (dwi.m_sm)); + c.print_to_pp (pp); + } +} + +/* Dump OBJ to OUTF, using OBJ's make_dump_widget member function. */ + +template <typename T> +void dump_to_file (const T &obj, FILE *outf) +{ + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + if (outf == stderr) + pp_show_color (&pp) = pp_show_color (global_dc->printer); + pp.buffer->stream = outf; + + text_art::theme *theme = global_dc->get_diagram_theme (); + dump_to_pp (obj, theme, &pp); + pp_flush (&pp); +} + +/* Dump OBJ to stderr, using OBJ's make_dump_widget member function. */ + +template <typename T> +void dump (const T &obj) +{ + dump_to_file (obj, stderr); +} + +} // namespace text_art + +#endif /* GCC_TEXT_ART_DUMP_H */ diff --git a/gcc/text-art/selftests.cc b/gcc/text-art/selftests.cc index bda1fc8e14a6..5b4679b0cda1 100644 --- a/gcc/text-art/selftests.cc +++ b/gcc/text-art/selftests.cc @@ -42,6 +42,7 @@ selftest::text_art_tests () text_art_ruler_cc_tests (); text_art_table_cc_tests (); text_art_widget_cc_tests (); + text_art_tree_widget_cc_tests (); } /* Implementation detail of ASSERT_CANVAS_STREQ. */ diff --git a/gcc/text-art/selftests.h b/gcc/text-art/selftests.h index 2be6279f8612..01e94539cede 100644 --- a/gcc/text-art/selftests.h +++ b/gcc/text-art/selftests.h @@ -34,6 +34,7 @@ extern void text_art_ruler_cc_tests (); extern void text_art_style_cc_tests (); extern void text_art_styled_string_cc_tests (); extern void text_art_table_cc_tests (); +extern void text_art_tree_widget_cc_tests (); extern void text_art_widget_cc_tests (); extern void text_art_tests (); diff --git a/gcc/text-art/theme.cc b/gcc/text-art/theme.cc index 1ee86c610289..e51b8ef7a84c 100644 --- a/gcc/text-art/theme.cc +++ b/gcc/text-art/theme.cc @@ -155,6 +155,15 @@ ascii_theme::get_cppchar (enum cell_kind kind) const return '+'; case cell_kind::CFG_FROM_DOWN_TO_RIGHT: return '+'; + + case cell_kind::TREE_CHILD_NON_FINAL: + return '+'; + case cell_kind::TREE_CHILD_FINAL: + return '`'; + case cell_kind::TREE_X_CONNECTOR: + return '-'; + case cell_kind::TREE_Y_CONNECTOR: + return '|'; } } @@ -240,5 +249,14 @@ unicode_theme::get_cppchar (enum cell_kind kind) const return 0x250c; /* "┌" U+250C: BOX DRAWINGS LIGHT DOWN AND RIGHT */ case cell_kind::CFG_FROM_DOWN_TO_RIGHT: return 0x2514; /* "└": U+2514: BOX DRAWINGS LIGHT UP AND RIGHT */ + + case cell_kind::TREE_CHILD_NON_FINAL: + return 0x251C; /* "├": U+251C: BOX DRAWINGS LIGHT VERTICAL AND RIGHT */ + case cell_kind::TREE_CHILD_FINAL: + return 0x2570; /* "╰": U+2570 BOX DRAWINGS LIGHT ARC UP AND RIGHT. */ + case cell_kind::TREE_X_CONNECTOR: + return 0x2500; /* "─": U+2500: BOX DRAWINGS LIGHT HORIZONTAL */ + case cell_kind::TREE_Y_CONNECTOR: + return 0x2502; /* "│": U+2502: BOX DRAWINGS LIGHT VERTICAL */ } } diff --git a/gcc/text-art/theme.h b/gcc/text-art/theme.h index e41fcc872301..dec09f8439be 100644 --- a/gcc/text-art/theme.h +++ b/gcc/text-art/theme.h @@ -81,7 +81,13 @@ class theme CFG_FROM_DOWN_TO_LEFT, /* e.g. "+". */ CFG_LEFT, /* e.g. "-". */ CFG_FROM_LEFT_TO_DOWN, /* e.g. "+". */ - CFG_FROM_DOWN_TO_RIGHT /* e.g. "+". */ + CFG_FROM_DOWN_TO_RIGHT, /* e.g. "+". */ + + /* Tree stuff. */ + TREE_CHILD_NON_FINAL, /* e.g. "├" or "+". */ + TREE_CHILD_FINAL, /* e.g. "╰" or "`". */ + TREE_X_CONNECTOR, /* e.g. "─" or "-". */ + TREE_Y_CONNECTOR /* e.g. "|" or "|". */ }; virtual ~theme () = default; diff --git a/gcc/text-art/tree-widget.cc b/gcc/text-art/tree-widget.cc new file mode 100644 index 000000000000..69282e46a44c --- /dev/null +++ b/gcc/text-art/tree-widget.cc @@ -0,0 +1,237 @@ +/* Tree diagrams. + Copyright (C) 2024 Free Software Foundation, Inc. + Contributed by David Malcolm <dmalc...@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" +#define INCLUDE_MEMORY +#define INCLUDE_VECTOR +#include "system.h" +#include "coretypes.h" +#include "pretty-print.h" +#include "selftest.h" +#include "make-unique.h" +#include "text-art/selftests.h" +#include "text-art/tree-widget.h" +#include "text-art/dump-widget-info.h" + +using namespace text_art; + +/* class text_art::tree_widget : public text_art::widget. */ + +static const int margin_width = 3; + +std::unique_ptr<tree_widget> +tree_widget::make (styled_string str, const theme &theme, style::id_t style_id) +{ + return ::make_unique <tree_widget> + (::make_unique <text_widget> (std::move (str)), + theme, + style_id); +} + +std::unique_ptr<tree_widget> +tree_widget::make (const dump_widget_info &dwi, pretty_printer *pp) +{ + return tree_widget::make (styled_string (dwi.m_sm, pp_formatted_text (pp)), + dwi.m_theme, + dwi.get_tree_style_id ()); +} + +std::unique_ptr<tree_widget> +tree_widget::make (const dump_widget_info &dwi, const char *str) +{ + return tree_widget::make (styled_string (dwi.m_sm, str), + dwi.m_theme, + dwi.get_tree_style_id ()); +} + +std::unique_ptr<tree_widget> +tree_widget::from_fmt (const dump_widget_info &dwi, + printer_fn format_decoder, + const char *fmt, ...) +{ + va_list ap; + va_start (ap, fmt); + styled_string styled_str + (styled_string::from_fmt_va (dwi.m_sm, format_decoder, fmt, &ap)); + va_end (ap); + return make (std::move (styled_str), dwi.m_theme, dwi.get_tree_style_id ()); +} + +const char * +tree_widget::get_desc () const +{ + return "tree_widget"; +} + +canvas::size_t +tree_widget::calc_req_size () +{ + canvas::size_t result (0, 0); + if (m_node) + { + canvas::size_t node_req_size = m_node->get_req_size (); + result.h += node_req_size.h; + result.w = std::max (result.w, node_req_size.w); + } + for (auto &child : m_children) + { + canvas::size_t child_req_size = child->get_req_size (); + result.h += child_req_size.h; + result.w = std::max (result.w, child_req_size.w + margin_width); + } + return result; +} + +void +tree_widget::update_child_alloc_rects () +{ + const int x = get_min_x (); + int y = get_min_y (); + if (m_node) + { + m_node->set_alloc_rect + (canvas::rect_t (canvas::coord_t (x, y), + canvas::size_t (get_alloc_w (), + m_node->get_req_h ()))); + y += m_node->get_req_h (); + } + for (auto &child : m_children) + { + child->set_alloc_rect + (canvas::rect_t (canvas::coord_t (x + margin_width, y), + canvas::size_t (get_alloc_w () - margin_width, + child->get_req_h ()))); + y += child->get_req_h (); + } +} + +void +tree_widget::paint_to_canvas (canvas &canvas) +{ + if (m_node) + m_node->paint_to_canvas (canvas); + const int min_x = get_min_x (); + const canvas::cell_t cell_child_non_final + (m_theme.get_cell (theme::cell_kind::TREE_CHILD_NON_FINAL, m_style_id)); + const canvas::cell_t cell_child_final + (m_theme.get_cell (theme::cell_kind::TREE_CHILD_FINAL, m_style_id)); + const canvas::cell_t cell_x_connector + (m_theme.get_cell (theme::cell_kind::TREE_X_CONNECTOR, m_style_id)); + const canvas::cell_t cell_y_connector + (m_theme.get_cell (theme::cell_kind::TREE_Y_CONNECTOR, m_style_id)); + size_t idx = 0; + for (auto &child : m_children) + { + child->paint_to_canvas (canvas); + + const bool last_child = (++idx == m_children.size ()); + canvas.paint (canvas::coord_t (min_x + 1, child->get_min_y ()), + cell_x_connector); + canvas.paint (canvas::coord_t (min_x, child->get_min_y ()), + last_child ? cell_child_final : cell_child_non_final); + if (!last_child) + for (int y = child->get_min_y () + 1; y <= child ->get_max_y (); y++) + canvas.paint (canvas::coord_t (min_x, y), cell_y_connector); + } +} + +#if CHECKING_P + +namespace selftest { + +static std::unique_ptr<tree_widget> +make_test_tree_widget (const dump_widget_info &dwi) +{ + std::unique_ptr<tree_widget> w + (tree_widget::from_fmt (dwi, nullptr, "Root")); + for (int i = 0; i < 3; i++) + { + std::unique_ptr<tree_widget> c + (tree_widget::from_fmt (dwi, nullptr, "Child %i", i)); + for (int j = 0; j < 3; j++) + c->add_child (tree_widget::from_fmt (dwi, nullptr, + "Grandchild %i %i", i, j)); + w->add_child (std::move (c)); + } + return w; +} + +static void +test_tree_widget () +{ + style_manager sm; + + style::id_t default_style_id (sm.get_or_create_id (style ())); + + { + ascii_theme theme; + dump_widget_info dwi (sm, theme, default_style_id); + canvas c (make_test_tree_widget (dwi)->to_canvas (sm)); + ASSERT_CANVAS_STREQ + (c, false, + ("Root\n" + "+- Child 0\n" + "| +- Grandchild 0 0\n" + "| +- Grandchild 0 1\n" + "| `- Grandchild 0 2\n" + "+- Child 1\n" + "| +- Grandchild 1 0\n" + "| +- Grandchild 1 1\n" + "| `- Grandchild 1 2\n" + "`- Child 2\n" + " +- Grandchild 2 0\n" + " +- Grandchild 2 1\n" + " `- Grandchild 2 2\n")); + } + + { + unicode_theme theme; + dump_widget_info dwi (sm, theme, default_style_id); + canvas c (make_test_tree_widget (dwi)->to_canvas (sm)); + ASSERT_CANVAS_STREQ + (c, false, + ("Root\n" + "├─ Child 0\n" + "│ ├─ Grandchild 0 0\n" + "│ ├─ Grandchild 0 1\n" + "│ ╰─ Grandchild 0 2\n" + "├─ Child 1\n" + "│ ├─ Grandchild 1 0\n" + "│ ├─ Grandchild 1 1\n" + "│ ╰─ Grandchild 1 2\n" + "╰─ Child 2\n" + " ├─ Grandchild 2 0\n" + " ├─ Grandchild 2 1\n" + " ╰─ Grandchild 2 2\n")); + } +} + +/* Run all selftests in this file. */ + +void +text_art_tree_widget_cc_tests () +{ + test_tree_widget (); +} + +} // namespace selftest + + +#endif /* #if CHECKING_P */ diff --git a/gcc/text-art/tree-widget.h b/gcc/text-art/tree-widget.h new file mode 100644 index 000000000000..73550ccf4eae --- /dev/null +++ b/gcc/text-art/tree-widget.h @@ -0,0 +1,83 @@ +/* Tree diagrams. + Copyright (C) 2024 Free Software Foundation, Inc. + Contributed by David Malcolm <dmalc...@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_TEXT_ART_TREE_WIDGET_H +#define GCC_TEXT_ART_TREE_WIDGET_H + +#include "text-art/canvas.h" +#include "text-art/widget.h" + +namespace text_art { + +class dump_widget_info; + +class tree_widget : public widget +{ +public: + tree_widget (std::unique_ptr<widget> node, + const theme &theme, + style::id_t style_id) + : m_node (std::move (node)), + m_theme (theme), + m_style_id (style_id) + { + } + + static std::unique_ptr<tree_widget> + make (styled_string str, const theme &theme, style::id_t style_id); + + static std::unique_ptr<tree_widget> + make (const dump_widget_info &dwi, pretty_printer *pp); + + static std::unique_ptr<tree_widget> + make (const dump_widget_info &dwi, const char *str); + + static std::unique_ptr<tree_widget> + from_fmt (const dump_widget_info &dwi, + printer_fn format_decoder, + const char *fmt, ...) + ATTRIBUTE_GCC_PPDIAG(3, 4); + + const char *get_desc () const override; + canvas::size_t calc_req_size () final override; + void update_child_alloc_rects () final override; + void paint_to_canvas (canvas &canvas) final override; + + void add_child (std::unique_ptr<widget> child) + { + if (child) + m_children.push_back (std::move (child)); + } + + size_t get_num_children () const + { + return m_children.size (); + } + +private: + std::unique_ptr<widget> m_node; + std::vector<std::unique_ptr<widget>> m_children; + const theme &m_theme; + style::id_t m_style_id; +}; + +} // namespace text_art + +#endif /* GCC_TEXT_ART_TREE_WIDGET_H */ -- 2.26.3