1 /* Text art visualizations within -fanalyzer.
2 Copyright (C) 2023 Free Software Foundation, Inc.
4 This file is part of GCC.
6 GCC is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3, or (at your option)
11 GCC is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GCC; see the file COPYING3. If not see
18 <http://www.gnu.org/licenses/>. */
21 #define INCLUDE_ALGORITHM
22 #define INCLUDE_MEMORY
25 #define INCLUDE_VECTOR
27 #include "coretypes.h"
28 #include "coretypes.h"
31 #include "basic-block.h"
33 #include "diagnostic.h"
35 #include "make-unique.h"
36 #include "tree-diagnostic.h" /* for default_tree_printer. */
37 #include "analyzer/analyzer.h"
38 #include "analyzer/region-model.h"
39 #include "analyzer/access-diagram.h"
40 #include "text-art/ruler.h"
41 #include "fold-const.h"
45 /* Consider this code:
48 where we've emitted a buffer overflow diagnostic like this:
49 out-of-bounds write from byte 40 till byte 43 but 'arr' ends at byte 40
51 We want to emit a diagram that visualizes:
52 - the spatial relationship between the valid region to access, versus
53 the region that was actually accessed: does it overlap, was it touching,
54 close, or far away? Was it before or after in memory? What are the
55 relative sizes involved?
56 - the direction of the access (read vs write)
58 The following code supports emitting diagrams similar to the following:
60 # +--------------------------------+
61 # |write from ‘x’ (type: ‘int32_t’)|
62 # +--------------------------------+
66 # +---------+-----------+-----------+ +--------------------------------+
67 # | [0] | ... | [9] | | after valid range |
68 # +---------+-----------+-----------+ | |
69 # | ‘arr’ (type: ‘int32_t[10]’) | | |
70 # +---------------------------------+ +--------------------------------+
71 # |~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~|
73 # +---------+--------+ +---------+---------+
74 # |capacity: 40 bytes| |overflow of 4 bytes|
75 # +------------------+ +-------------------+
77 where the diagram is laid out via table columns where each table column
78 represents either a range of bits/bytes, or is a spacing column (to highlight
79 the boundary between valid vs invalid accesses). The table columns can be
80 seen via -fanalyzer-debug-text-art. For example, here there are 5 table
81 columns ("tc0" through "tc4"):
83 # +---------+-----------+-----------+---+--------------------------------+
84 # | tc0 | tc1 | tc2 |tc3| tc4 |
85 # +---------+-----------+-----------+---+--------------------------------+
86 # |bytes 0-3|bytes 4-35 |bytes 36-39| | bytes 40-43 |
87 # +---------+-----------+-----------+ +--------------------------------+
89 # +--------------------------------+
90 # |write from ‘x’ (type: ‘int32_t’)|
91 # +--------------------------------+
95 # +---------+-----------+-----------+ +--------------------------------+
96 # | [0] | ... | [9] | | after valid range |
97 # +---------+-----------+-----------+ | |
98 # | ‘arr’ (type: ‘int32_t[10]’) | | |
99 # +---------------------------------+ +--------------------------------+
100 # |~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~|
102 # +---------+--------+ +---------+---------+
103 # |capacity: 40 bytes| |overflow of 4 bytes|
104 # +------------------+ +-------------------+
106 The diagram is built up from the following:
108 # +--------------------------------+
109 # | ITEM FOR SVALUE/ACCESSED REGION|
110 # +--------------------------------+
114 # +---------------------------------+ +--------------------------------+
115 # | VALID REGION | | INVALID ACCESS |
116 # +---------------------------------+ +--------------------------------+
118 # | VALID-VS-INVALID RULER |
120 i.e. a vbox_widget containing 4 child widgets laid out vertically:
121 - ALIGNED CHILD WIDGET: ITEM FOR SVALUE/ACCESSED REGION
123 - ALIGNED CHILD WIDGET: VALID AND INVALID ACCESSES
124 - VALID-VS-INVALID RULER.
126 A more complicated example, given this overflow:
128 strcpy (buf, LOREM_IPSUM);
130 01| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
131 02| |[0]|[1]|[2]|[3]|[4]|[5]| ... |[440]|[441]|[442]|[443]|[444]|[445]|
132 03| +---+---+---+---+---+---+ +-----+-----+-----+-----+-----+-----+
133 04| |'L'|'o'|'r'|'e'|'m'|' '| | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
134 05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
135 06| | string literal (type: 'char[446]') |
136 07| +----------------------------------------------------------------------+
137 08| | | | | | | | | | | | | | | |
138 09| | | | | | | | | | | | | | | |
139 10| v v v v v v v v v v v v v v v
140 11| +---+---------------------+----++--------------------------------------+
141 12| |[0]| ... |[99]|| after valid range |
142 13| +---+---------------------+----+| |
143 14| | 'buf' (type: 'char[100]') || |
144 15| +------------------------------++--------------------------------------+
145 16| |~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
147 18| +---------+---------+ +----------+----------+
148 19| |capacity: 100 bytes| |overflow of 346 bytes|
149 20| +-------------------+ +---------------------+
153 01| ALIGNED CHILD WIDGET (lines 01-07): (string_region_spatial_item)-+-----+
154 02| |[0]|[1]|[2]|[3]|[4]|[5]| ... |[440]|[441]|[442]|[443]|[444]|[445]|
155 03| +---+---+---+---+---+---+ +-----+-----+-----+-----+-----+-----+
156 04| |'L'|'o'|'r'|'e'|'m'|' '| | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
157 05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
158 06| | string literal (type: 'char[446]') |
159 07| +----------------------------------------------------------------------+
160 08| DIRECTION WIDGET (lines 08-10) | | | | | | |
161 09| | | | | | | | | | | | | | | |
162 10| v v v v v v v v v v v v v v v
163 11| ALIGNED CHILD WIDGET (lines 11-15)-------------------------------------+
164 12| VALID REGION ... |[99]|| INVALID ACCESS |
165 13| +---+---------------------+----+| |
166 14| | 'buf' (type: 'char[100]') || |
167 15| +------------------------------++--------------------------------------+
168 16| VALID-VS-INVALID RULER (lines 16-20): ~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
170 18| +---------+---------+ +----------+----------+
171 19| |capacity: 100 bytes| |overflow of 346 bytes|
172 20| +-------------------+ +---------------------+
174 We build the diagram in several phases:
175 - (1) we construct an access_diagram_impl widget. Within the ctor, we have
177 - (1.1) find all of the boundaries of interest
178 - (1.2) use the boundaries to build a bit_table_map, associating bit ranges
179 with table columns (e.g. "byte 0 is column 0, bytes 1-98 are column 2" etc)
180 - (1.3) create child widgets that share this table-based geometry
181 - (2) ask the widget for its size request
182 - (2.1) column widths and row heights for the table are computed by
183 access_diagram_impl::calc_req_size
184 - (2.2) child widgets request sizes based on these widths/heights
185 - (3) create a canvas of the appropriate size
186 - (4) paint the widget hierarchy to the canvas. */
189 using namespace text_art
;
194 fmt_styled_string (style_manager
&sm
,
195 const char *fmt
, ...)
196 ATTRIBUTE_GCC_DIAG(2, 3);
199 fmt_styled_string (style_manager
&sm
,
200 const char *fmt
, ...)
205 = styled_string::from_fmt_va (sm
, default_tree_printer
, fmt
, &ap
);
210 class access_diagram_impl
;
211 class bit_to_table_map
;
214 pp_bit_size_t (pretty_printer
*pp
, bit_size_t num_bits
)
216 if (num_bits
% BITS_PER_UNIT
== 0)
218 byte_size_t num_bytes
= num_bits
/ BITS_PER_UNIT
;
220 pp_printf (pp
, _("%wi byte"), num_bytes
.to_uhwi ());
222 pp_printf (pp
, _("%wi bytes"), num_bytes
.to_uhwi ());
227 pp_printf (pp
, _("%wi bit"), num_bits
.to_uhwi ());
229 pp_printf (pp
, _("%wi bits"), num_bits
.to_uhwi ());
234 get_access_size_str (style_manager
&sm
,
235 const access_operation
&op
,
236 access_range accessed_range
,
239 bit_size_expr num_bits
;
240 if (accessed_range
.get_size (op
.m_model
, &num_bits
))
247 num_bits
.print (&pp
);
249 if (op
.m_dir
== DIR_READ
)
250 return fmt_styled_string (sm
,
251 _("read of %qT (%s)"),
253 pp_formatted_text (&pp
));
255 return fmt_styled_string (sm
,
256 _("write of %qT (%s)"),
258 pp_formatted_text (&pp
));
260 if (op
.m_dir
== DIR_READ
)
261 return num_bits
.get_formatted_str (sm
,
262 _("read of %wi bit"),
263 _("read of %wi bits"),
264 _("read of %wi byte"),
265 _("read of %wi bytes"),
266 _("read of %qE bits"),
267 _("read of %qE bytes"));
269 return num_bits
.get_formatted_str (sm
,
270 _("write of %wi bit"),
271 _("write of %wi bits"),
272 _("write of %wi byte"),
273 _("write of %wi bytes"),
274 _("write of %qE bits"),
275 _("write of %qE bytes"));
280 if (op
.m_dir
== DIR_READ
)
281 return fmt_styled_string (sm
, _("read of %qT"), type
);
283 return fmt_styled_string (sm
, _("write of %qT"), type
);
286 if (op
.m_dir
== DIR_READ
)
287 return styled_string (sm
, _("read"));
289 return styled_string (sm
, _("write"));
292 /* Subroutine of clean_up_for_diagram. */
295 strip_any_cast (tree expr
)
297 if (TREE_CODE (expr
) == NOP_EXPR
298 || TREE_CODE (expr
) == NON_LVALUE_EXPR
)
299 expr
= TREE_OPERAND (expr
, 0);
303 /* Subroutine of clean_up_for_diagram. */
306 remove_ssa_names (tree expr
)
308 if (TREE_CODE (expr
) == SSA_NAME
309 && SSA_NAME_VAR (expr
))
310 return SSA_NAME_VAR (expr
);
311 tree t
= copy_node (expr
);
312 for (int i
= 0; i
< TREE_OPERAND_LENGTH (expr
); i
++)
313 TREE_OPERAND (t
, i
) = remove_ssa_names (TREE_OPERAND (expr
, i
));
317 /* We want to be able to print tree expressions from the analyzer,
318 which is in the middle end.
320 We could use the front-end pretty_printer's formatting routine,
322 (a) some have additional state in a pretty_printer subclass, so we'd
323 need to clone global_dc->printer
324 (b) the "aka" type information added by the C and C++ frontends are
325 too verbose when building a diagram, and there isn't a good way to ask
326 for a less verbose version of them.
328 Hence we use default_tree_printer.
329 However, we want to avoid printing SSA names, and instead print the
331 Ideally there would be a better tree printer for use by middle end
332 warnings, but as workaround, this function clones a tree, replacing
333 SSA names with the var names. */
336 clean_up_for_diagram (tree expr
)
338 tree without_ssa_names
= remove_ssa_names (expr
);
339 return strip_any_cast (without_ssa_names
);
342 /* struct bit_size_expr. */
344 text_art::styled_string
345 bit_size_expr::get_formatted_str (text_art::style_manager
&sm
,
346 const char *concrete_single_bit_fmt
,
347 const char *concrete_plural_bits_fmt
,
348 const char *concrete_single_byte_fmt
,
349 const char *concrete_plural_bytes_fmt
,
350 const char *symbolic_bits_fmt
,
351 const char *symbolic_bytes_fmt
) const
353 if (TREE_CODE (m_num_bits
) == INTEGER_CST
)
355 bit_size_t concrete_num_bits
= wi::to_offset (m_num_bits
);
356 if (concrete_num_bits
% BITS_PER_UNIT
== 0)
358 byte_size_t concrete_num_bytes
= concrete_num_bits
/ BITS_PER_UNIT
;
359 if (concrete_num_bytes
== 1)
360 return fmt_styled_string (sm
, concrete_single_byte_fmt
,
361 concrete_num_bytes
.to_uhwi ());
363 return fmt_styled_string (sm
, concrete_plural_bytes_fmt
,
364 concrete_num_bytes
.to_uhwi ());
368 if (concrete_num_bits
== 1)
369 return fmt_styled_string (sm
, concrete_single_bit_fmt
,
370 concrete_num_bits
.to_uhwi ());
372 return fmt_styled_string (sm
, concrete_plural_bits_fmt
,
373 concrete_num_bits
.to_uhwi ());
378 if (tree bytes_expr
= maybe_get_as_bytes ())
379 return fmt_styled_string (sm
,
381 clean_up_for_diagram (bytes_expr
));
382 return fmt_styled_string (sm
,
384 clean_up_for_diagram (m_num_bits
));
389 bit_size_expr::print (pretty_printer
*pp
) const
391 if (TREE_CODE (m_num_bits
) == INTEGER_CST
)
393 bit_size_t concrete_num_bits
= wi::to_offset (m_num_bits
);
394 pp_bit_size_t (pp
, concrete_num_bits
);
398 if (tree bytes_expr
= maybe_get_as_bytes ())
399 pp_printf (pp
, _("%qE bytes"), bytes_expr
);
401 pp_printf (pp
, _("%qE bits"), m_num_bits
);
406 bit_size_expr::maybe_get_as_bytes () const
408 switch (TREE_CODE (m_num_bits
))
414 const bit_size_t num_bits
= wi::to_offset (m_num_bits
);
415 if (num_bits
% BITS_PER_UNIT
!= 0)
417 const bit_size_t num_bytes
= num_bits
/ BITS_PER_UNIT
;
418 return wide_int_to_tree (size_type_node
, num_bytes
);
425 = bit_size_expr (TREE_OPERAND (m_num_bits
, 0));
426 tree op0_as_bytes
= op0
.maybe_get_as_bytes ();
430 = bit_size_expr (TREE_OPERAND (m_num_bits
, 1));
431 tree op1_as_bytes
= op1
.maybe_get_as_bytes ();
434 return fold_build2 (TREE_CODE (m_num_bits
), size_type_node
,
435 op0_as_bytes
, op1_as_bytes
);
441 = bit_size_expr (TREE_OPERAND (m_num_bits
, 1));
442 if (tree op1_as_bytes
= op1
.maybe_get_as_bytes ())
443 return fold_build2 (MULT_EXPR
, size_type_node
,
444 TREE_OPERAND (m_num_bits
, 0),
452 /* struct access_range. */
454 access_range::access_range (const region
*base_region
, const bit_range
&bits
)
455 : m_start (region_offset::make_concrete (base_region
,
456 bits
.get_start_bit_offset ())),
457 m_next (region_offset::make_concrete (base_region
,
458 bits
.get_next_bit_offset ()))
462 access_range::access_range (const region
*base_region
, const byte_range
&bytes
)
463 : m_start (region_offset::make_concrete (base_region
,
464 bytes
.get_start_bit_offset ())),
465 m_next (region_offset::make_concrete (base_region
,
466 bytes
.get_next_bit_offset ()))
470 access_range::access_range (const region
®
, region_model_manager
*mgr
)
471 : m_start (reg
.get_offset (mgr
)),
472 m_next (reg
.get_next_offset (mgr
))
477 access_range::get_size (const region_model
&model
, bit_size_expr
*out
) const
479 tree start_expr
= m_start
.calc_symbolic_bit_offset (model
);
482 tree next_expr
= m_next
.calc_symbolic_bit_offset (model
);
485 *out
= bit_size_expr (fold_build2 (MINUS_EXPR
, size_type_node
,
486 next_expr
, start_expr
));
491 access_range::contains_p (const access_range
&other
) const
493 return (m_start
<= other
.m_start
494 && other
.m_next
<= m_next
);
498 access_range::empty_p () const
500 bit_range
concrete_bits (0, 0);
501 if (!as_concrete_bit_range (&concrete_bits
))
503 return concrete_bits
.empty_p ();
507 access_range::dump_to_pp (pretty_printer
*pp
, bool simple
) const
509 if (m_start
.concrete_p () && m_next
.concrete_p ())
511 bit_range
bits (m_start
.get_bit_offset (),
512 m_next
.get_bit_offset () - m_start
.get_bit_offset ());
513 bits
.dump_to_pp (pp
);
516 pp_character (pp
, '[');
517 m_start
.dump_to_pp (pp
, simple
);
518 pp_string (pp
, " to ");
519 m_next
.dump_to_pp (pp
, simple
);
520 pp_character (pp
, ')');
524 access_range::dump (bool simple
) const
527 pp_format_decoder (&pp
) = default_tree_printer
;
528 pp_show_color (&pp
) = pp_show_color (global_dc
->printer
);
529 pp
.buffer
->stream
= stderr
;
530 dump_to_pp (&pp
, simple
);
536 access_range::log (const char *title
, logger
&logger
) const
538 logger
.start_log_line ();
539 logger
.log_partial ("%s: ", title
);
540 dump_to_pp (logger
.get_printer (), true);
541 logger
.end_log_line ();
544 /* struct access_operation. */
547 access_operation::get_valid_bits () const
549 const svalue
*capacity_in_bytes_sval
= m_model
.get_capacity (m_base_region
);
551 (region_offset::make_concrete (m_base_region
, 0),
552 region_offset::make_byte_offset (m_base_region
, capacity_in_bytes_sval
));
556 access_operation::get_actual_bits () const
558 return access_range (m_reg
, get_manager ());
561 /* If there are any bits accessed invalidly before the valid range,
562 return true and write their range to *OUT.
563 Return false if there aren't, or if there's a problem
564 (e.g. symbolic ranges. */
567 access_operation::maybe_get_invalid_before_bits (access_range
*out
) const
569 access_range
valid_bits (get_valid_bits ());
570 access_range
actual_bits (get_actual_bits ());
572 if (actual_bits
.m_start
>= valid_bits
.m_start
)
574 /* No part of accessed range is before the valid range. */
577 else if (actual_bits
.m_next
> valid_bits
.m_start
)
579 /* Get part of accessed range that's before the valid range. */
580 *out
= access_range (actual_bits
.m_start
, valid_bits
.m_start
);
585 /* Accessed range is fully before valid range. */
591 /* If there are any bits accessed invalidly after the valid range,
592 return true and write their range to *OUT.
593 Return false if there aren't, or if there's a problem. */
596 access_operation::maybe_get_invalid_after_bits (access_range
*out
) const
598 access_range
valid_bits (get_valid_bits ());
599 access_range
actual_bits (get_actual_bits ());
601 if (actual_bits
.m_next
<= valid_bits
.m_next
)
603 /* No part of accessed range is after the valid range. */
606 else if (actual_bits
.m_start
< valid_bits
.m_next
)
608 /* Get part of accessed range that's after the valid range. */
609 *out
= access_range (valid_bits
.m_next
, actual_bits
.m_next
);
614 /* Accessed range is fully after valid range. */
620 /* A class for capturing all of the region offsets of interest (both concrete
621 and symbolic), to help align everything in the diagram.
622 Boundaries can be soft or hard; hard boundaries are emphasized visually
623 (e.g. the boundary between valid vs invalid accesses).
625 Offsets in the boundaries are all expressed relative to the base
626 region of the access_operation. */
631 enum class kind
{ HARD
, SOFT
};
633 boundaries (const region
&base_reg
)
634 : m_base_reg (base_reg
)
638 void add (region_offset offset
, enum kind k
)
640 m_all_offsets
.insert (offset
);
642 m_hard_offsets
.insert (offset
);
645 void add (const access_range
&range
, enum kind kind
)
647 add (range
.m_start
, kind
);
648 add (range
.m_next
, kind
);
651 void add (const region
®
, region_model_manager
*mgr
, enum kind kind
)
653 add (access_range (reg
.get_offset (mgr
),
654 reg
.get_next_offset (mgr
)),
658 void add (const byte_range bytes
, enum kind kind
)
660 add (access_range (&m_base_reg
, bytes
), kind
);
663 void add_all_bytes_in_range (const byte_range
&bytes
)
665 for (byte_offset_t byte_idx
= bytes
.get_start_byte_offset ();
666 byte_idx
<= bytes
.get_next_byte_offset ();
667 byte_idx
= byte_idx
+ 1)
668 add (region_offset::make_concrete (&m_base_reg
, byte_idx
* 8),
672 void add_all_bytes_in_range (const access_range
&range
)
674 byte_range
bytes (0, 0);
675 bool valid
= range
.as_concrete_byte_range (&bytes
);
677 add_all_bytes_in_range (bytes
);
680 void log (logger
&logger
) const
682 logger
.log ("boundaries:");
683 logger
.inc_indent ();
684 for (auto offset
: m_all_offsets
)
686 enum kind k
= get_kind (offset
);
687 logger
.start_log_line ();
688 logger
.log_partial ("%s: ", (k
== kind::HARD
) ? "HARD" : "soft");
689 offset
.dump_to_pp (logger
.get_printer (), true);
690 logger
.end_log_line ();
692 logger
.dec_indent ();
695 enum kind
get_kind (region_offset offset
) const
697 gcc_assert (m_all_offsets
.find (offset
) != m_all_offsets
.end ());
698 if (m_hard_offsets
.find (offset
) != m_hard_offsets
.end ())
704 std::set
<region_offset
>::const_iterator
begin () const
706 return m_all_offsets
.begin ();
708 std::set
<region_offset
>::const_iterator
end () const
710 return m_all_offsets
.end ();
712 std::set
<region_offset
>::size_type
size () const
714 return m_all_offsets
.size ();
718 const region
&m_base_reg
;
719 std::set
<region_offset
> m_all_offsets
;
720 std::set
<region_offset
> m_hard_offsets
;
723 /* A widget that wraps a table but offloads column-width calculation
724 to a shared object, so that we can vertically line up multiple tables
725 and have them all align their columns.
729 01| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
730 02| |[0]|[1]|[2]|[3]|[4]|[5]| ... |[440]|[441]|[442]|[443]|[444]|[445]|
731 03| +---+---+---+---+---+---+ +-----+-----+-----+-----+-----+-----+
732 04| |'L'|'o'|'r'|'e'|'m'|' '| | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
733 05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
734 06| | string literal (type: 'char[446]') |
735 07| +----------------------------------------------------------------------+
736 08| | | | | | | | | | | | | | | |
737 09| | | | | | | | | | | | | | | |
738 10| v v v v v v v v v v v v v v v
739 11|+---+---------------------+----++--------------------------------------+
740 12||[0]| ... |[99]|| after valid range |
741 13|+---+---------------------+----+| |
742 14|| 'buf' (type: 'char[100]') || |
743 15|+------------------------------++--------------------------------------+
744 16||~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
746 18| +---------+---------+ +----------+----------+
747 19| |capacity: 100 bytes| |overflow of 346 bytes|
748 20| +-------------------+ +---------------------+
750 rows 01-07 and rows 11-15 are x_aligned_table_widget instances. */
752 class x_aligned_table_widget
: public leaf_widget
755 x_aligned_table_widget (table t
,
757 table_dimension_sizes
&col_widths
)
758 : m_table (std::move (t
)),
760 m_col_widths (col_widths
),
761 m_row_heights (t
.get_size ().h
),
762 m_cell_sizes (m_col_widths
, m_row_heights
),
763 m_tg (m_table
, m_cell_sizes
)
767 const char *get_desc () const override
769 return "x_aligned_table_widget";
772 canvas::size_t calc_req_size () final override
774 /* We don't compute the size requirements;
775 the parent should have done this. */
776 return m_tg
.get_canvas_size ();
779 void paint_to_canvas (canvas
&canvas
) final override
781 m_table
.paint_to_canvas (canvas
,
787 const table
&get_table () const { return m_table
; }
788 table_cell_sizes
&get_cell_sizes () { return m_cell_sizes
; }
789 void recalc_coords ()
791 m_tg
.recalc_coords ();
796 const theme
&m_theme
;
797 table_dimension_sizes
&m_col_widths
; // Reference to shared column widths
798 table_dimension_sizes m_row_heights
; // Unique row heights
799 table_cell_sizes m_cell_sizes
;
803 /* A widget for printing arrows between the accessed region
804 and the svalue, showing the direction of the access.
808 01| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
809 02| |[0]|[1]|[2]|[3]|[4]|[5]| ... |[440]|[441]|[442]|[443]|[444]|[445]|
810 03| +---+---+---+---+---+---+ +-----+-----+-----+-----+-----+-----+
811 04| |'L'|'o'|'r'|'e'|'m'|' '| | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
812 05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
813 06| | string literal (type: 'char[446]') |
814 07| +----------------------------------------------------------------------+
815 08| | | | | | | | | | | | | | | |
816 09| | | | | | | | | | | | | | | |
817 10| v v v v v v v v v v v v v v v
818 11|+---+---------------------+----++--------------------------------------+
819 12||[0]| ... |[99]|| after valid range |
820 13|+---+---------------------+----+| |
821 14|| 'buf' (type: 'char[100]') || |
822 15|+------------------------------++--------------------------------------+
823 16||~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
825 18| +---------+---------+ +----------+----------+
826 19| |capacity: 100 bytes| |overflow of 346 bytes|
827 20| +-------------------+ +---------------------+
829 rows 8-10 are the direction widget. */
831 class direction_widget
: public leaf_widget
834 direction_widget (const access_diagram_impl
&dia_impl
,
835 const bit_to_table_map
&btm
)
837 m_dia_impl (dia_impl
),
841 const char *get_desc () const override
843 return "direction_widget";
845 canvas::size_t calc_req_size () final override
847 /* Get our width from our siblings. */
848 return canvas::size_t (0, 3);
850 void paint_to_canvas (canvas
&canvas
) final override
;
853 const access_diagram_impl
&m_dia_impl
;
854 const bit_to_table_map
&m_btm
;
857 /* A widget for adding an x_ruler to a diagram based on table columns,
858 offloading column-width calculation to shared objects, so that the ruler
859 lines up with other tables in the diagram.
863 01| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
864 02| |[0]|[1]|[2]|[3]|[4]|[5]| ... |[440]|[441]|[442]|[443]|[444]|[445]|
865 03| +---+---+---+---+---+---+ +-----+-----+-----+-----+-----+-----+
866 04| |'L'|'o'|'r'|'e'|'m'|' '| | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
867 05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
868 06| | string literal (type: 'char[446]') |
869 07| +----------------------------------------------------------------------+
870 08| | | | | | | | | | | | | | | |
871 09| | | | | | | | | | | | | | | |
872 10| v v v v v v v v v v v v v v v
873 11|+---+---------------------+----++--------------------------------------+
874 12||[0]| ... |[99]|| after valid range |
875 13|+---+---------------------+----+| |
876 14|| 'buf' (type: 'char[100]') || |
877 15|+------------------------------++--------------------------------------+
878 16||~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
880 18| +---------+---------+ +----------+----------+
881 19| |capacity: 100 bytes| |overflow of 346 bytes|
882 20| +-------------------+ +---------------------+
884 rows 16-20 are the x_aligned_x_ruler_widget. */
886 class x_aligned_x_ruler_widget
: public leaf_widget
889 x_aligned_x_ruler_widget (const access_diagram_impl
&dia_impl
,
891 table_dimension_sizes
&col_widths
)
892 : m_dia_impl (dia_impl
),
894 m_col_widths (col_widths
)
898 const char *get_desc () const override
900 return "x_aligned_ruler_widget";
903 void add_range (const table::range_t
&x_range
,
905 style::id_t style_id
)
907 m_labels
.push_back (label (x_range
, std::move (text
), style_id
));
910 canvas::size_t calc_req_size () final override
912 x_ruler
r (make_x_ruler ());
913 return r
.get_size ();
916 void paint_to_canvas (canvas
&canvas
) final override
918 x_ruler
r (make_x_ruler ());
919 r
.paint_to_canvas (canvas
,
927 label (const table::range_t
&table_x_range
,
929 style::id_t style_id
)
930 : m_table_x_range (table_x_range
),
931 m_text (std::move (text
)),
932 m_style_id (style_id
)
935 table::range_t m_table_x_range
;
936 styled_string m_text
;
937 style::id_t m_style_id
;
940 x_ruler
make_x_ruler () const;
942 const access_diagram_impl
&m_dia_impl
;
943 const theme
&m_theme
;
944 table_dimension_sizes
&m_col_widths
;
945 std::vector
<label
> m_labels
;
948 /* A two-way mapping between access_ranges and table columns, for use by
949 spatial_item subclasses for creating tables.
950 For example when visualizing a bogus access of 'int arr[10];'
951 at 'arr[10]', we might have:
952 - table column 0 is "bytes 0-3" (for arr[0])
953 - table column 1 is "bytes 4-35" (for arr[1] through arr[8])
954 - table column 2 is "bytes 36-39 (for arr[9])
955 - table column 3 is blank to emphasize a hard boundary between
956 valid/invalid accesses.
957 - table column 4 is "bytes 40-44" (for arr[10])
959 We store this as a pair of maps from region_offset to table x; in
962 region offset table_x prev_table_x
963 bit 0 (aka byte 0) 0 (none)
964 bit 32 (aka byte 4) 1 0
965 bit 288 (aka byte 36) 2 1
966 bit 320 (aka byte 40) 4 2
967 bit 352 (aka byte 44) (none) (none)
969 so that e.g given the half-open byte range [0, 40)
970 we can determine the closed range of table x [0, 2]. */
972 class bit_to_table_map
975 /* Populate m_table_x_for_bit and m_bit_for_table_x. */
976 void populate (const boundaries
&boundaries
, logger
*logger
)
981 std::vector
<region_offset
> vec_boundaries (boundaries
.begin (),
984 /* Sort into an order that makes sense. */
985 std::sort (vec_boundaries
.begin (),
986 vec_boundaries
.end ());
990 logger
->log ("vec_boundaries");
991 logger
->inc_indent ();
992 for (unsigned idx
= 0; idx
< vec_boundaries
.size (); idx
++)
994 logger
->start_log_line ();
995 logger
->log_partial ("idx: %i: ", idx
);
996 vec_boundaries
[idx
].dump_to_pp (logger
->get_printer (), true);
997 logger
->end_log_line ();
999 logger
->dec_indent ();
1002 for (size_t idx
= 0; idx
< vec_boundaries
.size (); idx
++)
1004 const region_offset
&offset
= vec_boundaries
[idx
];
1005 if (idx
> 0 && (idx
+ 1) < vec_boundaries
.size ())
1007 if (boundaries
.get_kind (offset
) == boundaries::kind::HARD
)
1010 m_table_x_for_offset
[offset
] = table_x
;
1011 if ((idx
+ 1) < vec_boundaries
.size ())
1013 const region_offset
&next_offset
= vec_boundaries
[idx
+ 1];
1014 m_table_x_for_prev_offset
[next_offset
] = table_x
;
1015 m_range_for_table_x
[table_x
] = access_range (offset
, next_offset
);
1019 m_num_columns
= table_x
- 1;
1025 unsigned get_num_columns () const
1027 return m_num_columns
;
1030 table::range_t
get_table_x_for_range (const access_range
&range
) const
1032 return table::range_t (get_table_x_for_offset (range
.m_start
),
1033 get_table_x_for_prev_offset (range
.m_next
) + 1);
1036 table::rect_t
get_table_rect (const access_range
&range
,
1037 const int table_y
, const int table_h
) const
1039 const table::range_t
x_range (get_table_x_for_range (range
));
1040 return table::rect_t (table::coord_t (x_range
.start
, table_y
),
1041 table::size_t (x_range
.get_size (), table_h
));
1044 table::rect_t
get_table_rect (const region
*base_reg
,
1045 const bit_range
&bits
,
1046 const int table_y
, const int table_h
) const
1048 const access_range
range (base_reg
, bits
);
1049 return get_table_rect (range
, table_y
, table_h
);
1052 table::rect_t
get_table_rect (const region
*base_reg
,
1053 const byte_range
&bytes
,
1054 const int table_y
, const int table_h
) const
1056 return get_table_rect (base_reg
, bytes
.as_bit_range (), table_y
, table_h
);
1059 bool maybe_get_access_range_for_table_x (int table_x
,
1060 access_range
*out
) const
1062 auto slot
= m_range_for_table_x
.find (table_x
);
1063 if (slot
== m_range_for_table_x
.end ())
1065 *out
= slot
->second
;
1069 void log (logger
&logger
) const
1071 logger
.log ("table columns");
1072 logger
.inc_indent ();
1073 for (unsigned table_x
= 0; table_x
< get_num_columns (); table_x
++)
1075 logger
.start_log_line ();
1076 logger
.log_partial ("table_x: %i", table_x
);
1077 access_range
range_for_column (NULL
, bit_range (0, 0));
1078 if (maybe_get_access_range_for_table_x (table_x
, &range_for_column
))
1080 logger
.log_partial (": range: ");
1081 range_for_column
.dump_to_pp (logger
.get_printer (), true);
1083 logger
.end_log_line ();
1085 logger
.dec_indent ();
1089 int get_table_x_for_offset (region_offset offset
) const
1091 auto slot
= m_table_x_for_offset
.find (offset
);
1093 /* If this fails, then we probably failed to fully populate m_boundaries
1094 in find_boundaries. */
1095 gcc_assert (slot
!= m_table_x_for_offset
.end ());
1097 return slot
->second
;
1100 int get_table_x_for_prev_offset (region_offset offset
) const
1102 auto slot
= m_table_x_for_prev_offset
.find (offset
);
1104 /* If this fails, then we probably failed to fully populate m_boundaries
1105 in find_boundaries. */
1106 gcc_assert (slot
!= m_table_x_for_prev_offset
.end ());
1108 return slot
->second
;
1111 std::map
<region_offset
, int> m_table_x_for_offset
;
1112 std::map
<region_offset
, int> m_table_x_for_prev_offset
;
1113 std::map
<int, access_range
> m_range_for_table_x
;
1114 unsigned m_num_columns
;
1117 /* Base class for something in the diagram that participates
1118 in two steps of diagram creation:
1119 (a) populating a boundaries instance with the boundaries of interest
1120 (b) creating a table instance for itself.
1122 Offsets in the boundaries are all expressed relative to the base
1123 region of the access_operation. */
1128 virtual ~spatial_item () {}
1129 virtual void add_boundaries (boundaries
&out
, logger
*) const = 0;
1131 virtual table
make_table (const bit_to_table_map
&btm
,
1132 style_manager
&sm
) const = 0;
1135 /* Subclass of spatial_item for visualizing the region of memory
1136 that's valid to access relative to the base region of region accessed in
1139 class valid_region_spatial_item
: public spatial_item
1142 valid_region_spatial_item (const access_operation
&op
,
1143 diagnostic_event_id_t region_creation_event_id
)
1145 m_region_creation_event_id (region_creation_event_id
)
1148 void add_boundaries (boundaries
&out
, logger
*logger
) const final override
1151 access_range valid_bits
= m_op
.get_valid_bits ();
1154 logger
->start_log_line ();
1155 logger
->log_partial ("valid bits: ");
1156 valid_bits
.dump_to_pp (logger
->get_printer (), true);
1157 logger
->end_log_line ();
1159 out
.add (valid_bits
, boundaries::kind::HARD
);
1161 /* Support for showing first and final element in array types. */
1162 if (tree base_type
= m_op
.m_base_region
->get_type ())
1163 if (TREE_CODE (base_type
) == ARRAY_TYPE
)
1166 logger
->log ("showing first and final element in array type");
1167 region_model_manager
*mgr
= m_op
.m_model
.get_manager ();
1168 tree domain
= TYPE_DOMAIN (base_type
);
1169 if (TYPE_MIN_VALUE (domain
) && TYPE_MAX_VALUE (domain
))
1171 const svalue
*min_idx_sval
1172 = mgr
->get_or_create_constant_svalue (TYPE_MIN_VALUE (domain
));
1173 const svalue
*max_idx_sval
1174 = mgr
->get_or_create_constant_svalue (TYPE_MAX_VALUE (domain
));
1175 const region
*min_element
=
1176 mgr
->get_element_region (m_op
.m_base_region
,
1177 TREE_TYPE (base_type
),
1179 out
.add (*min_element
, mgr
, boundaries::kind::SOFT
);
1180 const region
*max_element
=
1181 mgr
->get_element_region (m_op
.m_base_region
,
1182 TREE_TYPE (base_type
),
1184 out
.add (*max_element
, mgr
, boundaries::kind::SOFT
);
1189 /* Subroutine of make_table when base region has ARRAY_TYPE. */
1190 void add_array_elements_to_table (table
&t
,
1191 const bit_to_table_map
&btm
,
1192 style_manager
&sm
) const
1194 tree base_type
= m_op
.m_base_region
->get_type ();
1195 gcc_assert (TREE_CODE (base_type
) == ARRAY_TYPE
);
1197 tree domain
= TYPE_DOMAIN (base_type
);
1198 if (!(TYPE_MIN_VALUE (domain
) && TYPE_MAX_VALUE (domain
)))
1201 region_model_manager
* const mgr
= m_op
.get_manager ();
1202 const int table_y
= 0;
1203 const int table_h
= 1;
1204 const table::range_t
table_y_range (table_y
, table_y
+ table_h
);
1207 const svalue
*min_idx_sval
1208 = mgr
->get_or_create_constant_svalue (TYPE_MIN_VALUE (domain
));
1209 const region
*min_element
= mgr
->get_element_region (m_op
.m_base_region
,
1210 TREE_TYPE (base_type
),
1212 const access_range
min_element_range (*min_element
, mgr
);
1213 const table::range_t min_element_x_range
1214 = btm
.get_table_x_for_range (min_element_range
);
1216 t
.set_cell_span (table::rect_t (min_element_x_range
,
1218 fmt_styled_string (sm
, "[%E]",
1219 TYPE_MIN_VALUE (domain
)));
1221 const svalue
*max_idx_sval
1222 = mgr
->get_or_create_constant_svalue (TYPE_MAX_VALUE (domain
));
1223 const region
*max_element
= mgr
->get_element_region (m_op
.m_base_region
,
1224 TREE_TYPE (base_type
),
1226 if (min_element
== max_element
)
1227 return; // 1-element array
1229 const access_range
max_element_range (*max_element
, mgr
);
1230 const table::range_t max_element_x_range
1231 = btm
.get_table_x_for_range (max_element_range
);
1232 t
.set_cell_span (table::rect_t (max_element_x_range
,
1234 fmt_styled_string (sm
, "[%E]",
1235 TYPE_MAX_VALUE (domain
)));
1237 const table::range_t
other_elements_x_range (min_element_x_range
.next
,
1238 max_element_x_range
.start
);
1239 if (other_elements_x_range
.get_size () > 0)
1240 t
.set_cell_span (table::rect_t (other_elements_x_range
, table_y_range
),
1241 styled_string (sm
, "..."));
1244 table
make_table (const bit_to_table_map
&btm
,
1245 style_manager
&sm
) const final override
1247 table
t (table::size_t (btm
.get_num_columns (), 1));
1249 if (tree base_type
= m_op
.m_base_region
->get_type ())
1250 if (TREE_CODE (base_type
) == ARRAY_TYPE
)
1251 add_array_elements_to_table (t
, btm
, sm
);
1253 access_range valid_bits
= m_op
.get_valid_bits ();
1254 const int table_y
= t
.get_size ().h
- 1;
1255 const int table_h
= 1;
1256 table::rect_t rect
= btm
.get_table_rect (valid_bits
, table_y
, table_h
);
1258 switch (m_op
.m_base_region
->get_kind ())
1261 s
= styled_string (sm
, _("region"));
1265 const decl_region
*decl_reg
1266 = as_a
<const decl_region
*> (m_op
.m_base_region
);
1267 tree decl
= decl_reg
->get_decl ();
1268 s
= fmt_styled_string (sm
, "%qE (type: %qT)",
1273 case RK_HEAP_ALLOCATED
:
1275 if (m_region_creation_event_id
.known_p ())
1276 s
= fmt_styled_string (sm
, _("buffer allocated on heap at %@"),
1277 &m_region_creation_event_id
);
1279 s
= styled_string (sm
, _("heap-allocated buffer"));
1284 if (m_region_creation_event_id
.known_p ())
1285 s
= fmt_styled_string (sm
, _("buffer allocated on stack at %@"),
1286 &m_region_creation_event_id
);
1288 s
= styled_string (sm
, _("stack-allocated buffer"));
1293 const string_region
*string_reg
1294 = as_a
<const string_region
*> (m_op
.m_base_region
);
1295 tree string_cst
= string_reg
->get_string_cst ();
1296 s
= fmt_styled_string (sm
, _("string literal (type: %qT)"),
1297 TREE_TYPE (string_cst
));
1301 t
.set_cell_span (rect
, std::move (s
));
1307 const access_operation
&m_op
;
1308 diagnostic_event_id_t m_region_creation_event_id
;
1311 /* Subclass of spatial_item for visualizing the region of memory
1312 that's actually accessed by the read or write, for reads and
1313 for write cases where we don't know the svalue written. */
1315 class accessed_region_spatial_item
: public spatial_item
1318 accessed_region_spatial_item (const access_operation
&op
) : m_op (op
) {}
1320 void add_boundaries (boundaries
&out
, logger
*logger
) const final override
1323 access_range actual_bits
= m_op
.get_actual_bits ();
1326 logger
->start_log_line ();
1327 logger
->log_partial ("actual bits: ");
1328 actual_bits
.dump_to_pp (logger
->get_printer (), true);
1329 logger
->end_log_line ();
1331 out
.add (actual_bits
, boundaries::kind::HARD
);
1334 table
make_table (const bit_to_table_map
&btm
,
1335 style_manager
&sm
) const final override
1337 table
t (table::size_t (btm
.get_num_columns (), 1));
1339 access_range actual_bits
= m_op
.get_actual_bits ();
1340 const int table_y
= 0;
1341 const int table_h
= 1;
1342 table::rect_t rect
= btm
.get_table_rect (actual_bits
, table_y
, table_h
);
1343 t
.set_cell_span (rect
, styled_string (get_label_string (sm
)));
1349 styled_string
get_label_string (style_manager
&sm
) const
1351 const access_range
accessed_bits (m_op
.get_actual_bits ());
1352 return get_access_size_str (sm
,
1355 m_op
.m_reg
.get_type ());
1358 const access_operation
&m_op
;
1361 /* Subclass of spatial_item for when we know the svalue being written
1362 to the accessed region.
1363 Can be subclassed to give visualizations of specific kinds of svalue. */
1365 class svalue_spatial_item
: public spatial_item
1368 static std::unique_ptr
<svalue_spatial_item
> make (const access_operation
&op
,
1370 access_range actual_bits
,
1371 const theme
&theme
);
1373 svalue_spatial_item (const access_operation
&op
,
1375 access_range actual_bits
)
1376 : m_op (op
), m_sval (sval
), m_actual_bits (actual_bits
)
1379 void add_boundaries (boundaries
&out
, logger
*logger
) const override
1382 out
.add (m_actual_bits
, boundaries::kind::HARD
);
1385 table
make_table (const bit_to_table_map
&btm
,
1386 style_manager
&sm
) const override
1388 table
t (table::size_t (btm
.get_num_columns (), 0));
1390 const int table_y
= t
.add_row ();
1391 const int table_h
= 1;
1392 table::rect_t rect
= btm
.get_table_rect (m_actual_bits
, table_y
, table_h
);
1393 t
.set_cell_span (rect
, styled_string (get_label_string (sm
)));
1398 styled_string
get_label_string (style_manager
&sm
) const
1400 tree rep_tree
= m_op
.m_model
.get_representative_tree (&m_sval
);
1403 if (TREE_CODE (rep_tree
) == SSA_NAME
)
1404 rep_tree
= SSA_NAME_VAR (rep_tree
);
1405 switch (TREE_CODE (rep_tree
))
1410 return fmt_styled_string (sm
, _("write of %<(%T) %E%>"),
1411 TREE_TYPE (rep_tree
),
1416 return fmt_styled_string (sm
, _("write from %qE (type: %qT)"),
1418 TREE_TYPE (rep_tree
));
1423 const access_range
accessed_bits (m_op
.get_actual_bits ());
1424 return get_access_size_str (sm
,
1427 m_sval
.get_type ());
1430 const access_operation
&m_op
;
1431 const svalue
&m_sval
;
1432 access_range m_actual_bits
;
1435 /* Subclass of svalue_spatial_item for initial_svalue of a string_region
1436 i.e. for string literals.
1438 There are three cases:
1439 (a) for long strings, show just the head and tail of the string,
1441 +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
1442 |[0]|[1]|[2]|[3]|[4]|[5]| |[440]|[441]|[442]|[443]|[444]|[445]|
1443 +---+---+---+---+---+---+ ... +-----+-----+-----+-----+-----+-----+
1444 |‘L’|‘o’|‘r’|‘e’|‘m’|‘ ’| | ‘o’ | ‘r’ | ‘u’ | ‘m’ | ‘.’ | NUL |
1445 +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
1446 | string literal (type: ‘char[446]’) |
1447 +----------------------------------------------------------------------+
1448 (b) For sufficiently short strings, show the full string:
1449 +----------+---------+---------+---------+---------+ +-----------------+
1450 | [0] | [1] | [2] | [3] | [4] | | [5] |
1451 +----------+---------+---------+---------+---------+ +-----------------+
1452 | ‘h’ | ‘e’ | ‘l’ | ‘l’ | ‘o’ | | NUL |
1453 +----------+---------+---------+---------+---------+-+-----------------+
1454 | string literal (type: ‘char[6]’) |
1455 +----------------------------------------------------------------------+
1456 (c) for non-ASCII strings that are short enough to show the full string,
1457 show how unicode code points of the bytes decoded as UTF-8:
1458 +-----+-----+-----+----+----++----+----+----+----+----+----+----+------+
1459 | [0] | [1] | [2] |[3] |[4] ||[5] |[6] |[7] |[8] |[9] |[10]|[11]| [12] |
1460 +-----+-----+-----+----+----++----+----+----+----+----+----+----+------+
1461 |0xe6 |0x96 |0x87 |0xe5|0xad||0x97|0xe5|0x8c|0x96|0xe3|0x81|0x91| 0x00 |
1462 +-----+-----+-----+----+----++----+----+----+----+----+----+----+------+
1463 | U+6587 | U+5b57 | U+5316 | U+3051 |U+0000|
1464 +-----------------+---------------+--------------+--------------+------+
1465 | string literal (type: ‘char[13]’) |
1466 +----------------------------------------------------------------------+
1467 and show the characters themselves if unicode is supported and they are not
1469 ┌─────┬─────┬─────┬────┬────┐┌────┬────┬────┬────┬────┬────┬────┬──────┐
1470 │ [0] │ [1] │ [2] │[3] │[4] ││[5] │[6] │[7] │[8] │[9] │[10]│[11]│ [12] │
1471 ├─────┼─────┼─────┼────┼────┤├────┼────┼────┼────┼────┼────┼────┼──────┤
1472 │0xe6 │0x96 │0x87 │0xe5│0xad││0x97│0xe5│0x8c│0x96│0xe3│0x81│0x91│ 0x00 │
1473 ├─────┴─────┴─────┼────┴────┴┴────┼────┴────┴────┼────┴────┴────┼──────┤
1474 │ U+6587 │ U+5b57 │ U+5316 │ U+3051 │U+0000│
1475 ├─────────────────┼───────────────┼──────────────┼──────────────┼──────┤
1476 │ 文 │ 字 │ 化 │ け │ NUL │
1477 ├─────────────────┴───────────────┴──────────────┴──────────────┴──────┤
1478 │ string literal (type: ‘char[13]’) │
1479 └──────────────────────────────────────────────────────────────────────┘
1482 class string_region_spatial_item
: public svalue_spatial_item
1485 string_region_spatial_item (const access_operation
&op
,
1487 access_range actual_bits
,
1488 const string_region
&string_reg
,
1490 : svalue_spatial_item (op
, sval
, actual_bits
),
1491 m_string_reg (string_reg
),
1493 m_ellipsis_threshold (param_analyzer_text_art_string_ellipsis_threshold
),
1494 m_ellipsis_head_len (param_analyzer_text_art_string_ellipsis_head_len
),
1495 m_ellipsis_tail_len (param_analyzer_text_art_string_ellipsis_tail_len
),
1496 m_show_full_string (calc_show_full_string ()),
1497 m_show_utf8 (m_show_full_string
&& !pure_ascii_p ())
1501 void add_boundaries (boundaries
&out
, logger
*logger
) const override
1504 out
.add (m_actual_bits
, boundaries::kind::HARD
);
1506 tree string_cst
= get_string_cst ();
1507 /* TREE_STRING_LENGTH is sizeof, not strlen. */
1508 if (m_show_full_string
)
1509 out
.add_all_bytes_in_range (m_actual_bits
);
1512 byte_range
bytes (0, 0);
1513 bool valid
= m_actual_bits
.as_concrete_byte_range (&bytes
);
1515 byte_range
head_of_string (bytes
.get_start_byte_offset (),
1516 m_ellipsis_head_len
);
1517 out
.add_all_bytes_in_range (head_of_string
);
1518 byte_range tail_of_string
1519 ((bytes
.get_start_byte_offset ()
1520 + TREE_STRING_LENGTH (string_cst
)
1521 - m_ellipsis_tail_len
),
1522 m_ellipsis_tail_len
);
1523 out
.add_all_bytes_in_range (tail_of_string
);
1524 /* Adding the above pair of ranges will also effectively add
1525 the boundaries of the range of ellipsized chars, as they're
1526 exactly in between head_of_string and tail_of_string. */
1530 table
make_table (const bit_to_table_map
&btm
,
1531 style_manager
&sm
) const override
1533 table
t (table::size_t (btm
.get_num_columns (), 0));
1535 const int byte_idx_table_y
= t
.add_row ();
1536 const int byte_val_table_y
= t
.add_row ();
1538 byte_range
bytes (0, 0);
1539 bool valid
= m_actual_bits
.as_concrete_byte_range (&bytes
);
1541 tree string_cst
= get_string_cst ();
1542 if (m_show_full_string
)
1544 for (byte_offset_t byte_idx_within_cluster
1545 = bytes
.get_start_byte_offset ();
1546 byte_idx_within_cluster
< bytes
.get_next_byte_offset ();
1547 byte_idx_within_cluster
= byte_idx_within_cluster
+ 1)
1549 (t
, btm
, sm
, byte_idx_within_cluster
,
1550 byte_idx_within_cluster
- bytes
.get_start_byte_offset (),
1551 byte_idx_table_y
, byte_val_table_y
);
1555 const bool show_unichars
= m_theme
.unicode_p ();
1556 const int utf8_code_point_table_y
= t
.add_row ();
1557 int utf8_character_table_y
;
1559 utf8_character_table_y
= t
.add_row ();
1561 /* We don't actually want the display widths here, but
1562 it's an easy way to decode UTF-8. */
1563 cpp_char_column_policy
policy (8, cpp_wcwidth
);
1564 cpp_display_width_computation
dw (TREE_STRING_POINTER (string_cst
),
1565 TREE_STRING_LENGTH (string_cst
),
1569 cpp_decoded_char decoded_char
;
1570 dw
.process_next_codepoint (&decoded_char
);
1572 if (!decoded_char
.m_valid_ch
)
1574 size_t start_byte_idx
1575 = decoded_char
.m_start_byte
- TREE_STRING_POINTER (string_cst
);
1576 byte_size_t size_in_bytes
1577 = decoded_char
.m_next_byte
- decoded_char
.m_start_byte
;
1578 byte_range cluster_bytes_for_codepoint
1579 (start_byte_idx
+ bytes
.get_start_byte_offset (),
1582 const table::rect_t code_point_table_rect
1583 = btm
.get_table_rect (&m_string_reg
,
1584 cluster_bytes_for_codepoint
,
1585 utf8_code_point_table_y
, 1);
1587 sprintf (buf
, "U+%04x", decoded_char
.m_ch
);
1588 t
.set_cell_span (code_point_table_rect
,
1589 styled_string (sm
, buf
));
1593 const table::rect_t character_table_rect
1594 = btm
.get_table_rect (&m_string_reg
,
1595 cluster_bytes_for_codepoint
,
1596 utf8_character_table_y
, 1);
1597 if (cpp_is_printable_char (decoded_char
.m_ch
))
1598 t
.set_cell_span (character_table_rect
,
1599 styled_string (decoded_char
.m_ch
));
1600 else if (decoded_char
.m_ch
== 0)
1601 t
.set_cell_span (character_table_rect
,
1602 styled_string (sm
, "NUL"));
1604 t
.set_cell_span (character_table_rect
,
1605 styled_string (sm
, ""));
1612 /* Head of string. */
1613 for (int byte_idx
= 0; byte_idx
< m_ellipsis_head_len
; byte_idx
++)
1614 add_column_for_byte (t
, btm
, sm
,
1615 byte_idx
+ bytes
.get_start_byte_offset (),
1617 byte_idx_table_y
, byte_val_table_y
);
1619 /* Ellipsis (two rows high). */
1620 const byte_range ellipsis_bytes
1621 (m_ellipsis_head_len
+ bytes
.get_start_byte_offset (),
1622 TREE_STRING_LENGTH (string_cst
)
1623 - (m_ellipsis_head_len
+ m_ellipsis_tail_len
));
1624 const table::rect_t table_rect
1625 = btm
.get_table_rect (&m_string_reg
, ellipsis_bytes
,
1626 byte_idx_table_y
, 2);
1627 t
.set_cell_span(table_rect
, styled_string (sm
, "..."));
1629 /* Tail of string. */
1631 = (TREE_STRING_LENGTH (string_cst
) - m_ellipsis_tail_len
);
1632 byte_idx
< TREE_STRING_LENGTH (string_cst
);
1634 add_column_for_byte (t
, btm
, sm
,
1635 byte_idx
+ bytes
.get_start_byte_offset (),
1637 byte_idx_table_y
, byte_val_table_y
);
1640 const int summary_table_y
= t
.add_row ();
1641 t
.set_cell_span (btm
.get_table_rect (&m_string_reg
, bytes
,
1642 summary_table_y
, 1),
1643 fmt_styled_string (sm
,
1644 _("string literal (type: %qT)"),
1645 TREE_TYPE (string_cst
)));
1650 tree
get_string_cst () const { return m_string_reg
.get_string_cst (); }
1653 bool calc_show_full_string () const
1655 tree string_cst
= get_string_cst ();
1656 if (TREE_STRING_LENGTH (string_cst
) < m_ellipsis_threshold
)
1658 if (TREE_STRING_LENGTH (string_cst
) <
1659 (m_ellipsis_head_len
+ m_ellipsis_tail_len
))
1664 bool pure_ascii_p () const
1666 tree string_cst
= get_string_cst ();
1667 for (unsigned byte_idx
= 0;
1668 byte_idx
< (unsigned) TREE_STRING_LENGTH (string_cst
);
1671 unsigned char ch
= TREE_STRING_POINTER (string_cst
)[byte_idx
];
1678 void add_column_for_byte (table
&t
, const bit_to_table_map
&btm
,
1680 const byte_offset_t byte_idx_within_cluster
,
1681 const byte_offset_t byte_idx_within_string
,
1682 const int byte_idx_table_y
,
1683 const int byte_val_table_y
) const
1685 tree string_cst
= get_string_cst ();
1686 gcc_assert (byte_idx_within_string
>= 0);
1687 gcc_assert (byte_idx_within_string
< TREE_STRING_LENGTH (string_cst
));
1689 const byte_range
bytes (byte_idx_within_cluster
, 1);
1690 if (1) // show_byte_indices
1692 const table::rect_t idx_table_rect
1693 = btm
.get_table_rect (&m_string_reg
, bytes
, byte_idx_table_y
, 1);
1694 t
.set_cell_span (idx_table_rect
,
1695 fmt_styled_string (sm
, "[%li]",
1696 byte_idx_within_string
.ulow ()));
1700 = TREE_STRING_POINTER (string_cst
)[byte_idx_within_string
.ulow ()];
1701 const table::rect_t val_table_rect
1702 = btm
.get_table_rect (&m_string_reg
, bytes
, byte_val_table_y
, 1);
1703 table_cell_content
content (make_cell_content_for_byte (sm
, byte_val
));
1704 t
.set_cell_span (val_table_rect
, std::move (content
));
1707 table_cell_content
make_cell_content_for_byte (style_manager
&sm
,
1708 unsigned char byte_val
) const
1712 if (byte_val
== '\0')
1713 return styled_string (sm
, "NUL");
1714 else if (byte_val
< 0x80)
1715 if (ISPRINT (byte_val
))
1716 return fmt_styled_string (sm
, "%qc", byte_val
);
1719 sprintf (buf
, "0x%02x", byte_val
);
1720 return styled_string (sm
, buf
);
1723 const string_region
&m_string_reg
;
1724 const theme
&m_theme
;
1725 const int m_ellipsis_threshold
;
1726 const int m_ellipsis_head_len
;
1727 const int m_ellipsis_tail_len
;
1728 const bool m_show_full_string
;
1729 const bool m_show_utf8
;
1732 std::unique_ptr
<svalue_spatial_item
>
1733 svalue_spatial_item::make (const access_operation
&op
,
1735 access_range actual_bits
,
1738 if (const initial_svalue
*initial_sval
= sval
.dyn_cast_initial_svalue ())
1739 if (const string_region
*string_reg
1740 = initial_sval
->get_region ()->dyn_cast_string_region ())
1741 return make_unique
<string_region_spatial_item
> (op
, sval
, actual_bits
,
1742 *string_reg
, theme
);
1743 return make_unique
<svalue_spatial_item
> (op
, sval
, actual_bits
);
1746 /* Widget subclass implementing access diagrams. */
1748 class access_diagram_impl
: public vbox_widget
1751 access_diagram_impl (const access_operation
&op
,
1752 diagnostic_event_id_t region_creation_event_id
,
1757 m_region_creation_event_id (region_creation_event_id
),
1762 m_valid_region_spatial_item (op
, region_creation_event_id
),
1763 m_accessed_region_spatial_item (op
),
1765 m_calc_req_size_called (false)
1771 access_range invalid_before_bits
;
1772 if (op
.maybe_get_invalid_before_bits (&invalid_before_bits
))
1773 invalid_before_bits
.log ("invalid before range", *logger
);
1774 access_range invalid_after_bits
;
1775 if (op
.maybe_get_invalid_after_bits (&invalid_after_bits
))
1776 invalid_after_bits
.log ("invalid after range", *logger
);
1780 logger
->start_log_line ();
1781 logger
->log_partial ("sval_hint: ");
1782 op
.m_sval_hint
->dump_to_pp (logger
->get_printer (), true);
1783 logger
->end_log_line ();
1787 /* Register painting styles. */
1790 valid_style
.m_fg_color
= style::named_color::GREEN
;
1791 valid_style
.m_bold
= true;
1792 m_valid_style_id
= m_sm
.get_or_create_id (valid_style
);
1794 style invalid_style
;
1795 invalid_style
.m_fg_color
= style::named_color::RED
;
1796 invalid_style
.m_bold
= true;
1797 m_invalid_style_id
= m_sm
.get_or_create_id (invalid_style
);
1802 access_range actual_bits
= m_op
.get_actual_bits ();
1803 m_svalue_spatial_item
= svalue_spatial_item::make (m_op
,
1810 First, figure out all of the boundaries of interest.
1811 Then use that to build child widgets showing the regions of interest,
1812 with a common tabular layout. */
1814 m_boundaries
= find_boundaries ();
1816 m_boundaries
->log (*logger
);
1818 /* Populate m_table_x_for_bit and m_bit_for_table_x.
1819 Each table column represents the range [offset, next_offset).
1820 We don't create a column in the table for the final offset, but we
1821 do populate it, so that looking at the table_x of one beyond the
1822 final table column gives us the upper bound offset. */
1823 m_btm
.populate (*m_boundaries
, logger
);
1825 /* Gracefully reject cases where the boundary sorting has gone wrong
1826 (due to awkward combinations of symbolic values). */
1828 table::range_t actual_bits_x_range
1829 = m_btm
.get_table_x_for_range (m_op
.get_actual_bits ());
1830 if (actual_bits_x_range
.get_size () <= 0)
1833 logger
->log ("giving up: bad table columns for actual_bits");
1837 table::range_t valid_bits_x_range
1838 = m_btm
.get_table_x_for_range (m_op
.get_valid_bits ());
1839 if (valid_bits_x_range
.get_size () <= 0)
1842 logger
->log ("giving up: bad table columns for valid_bits");
1849 = make_unique
<table_dimension_sizes
> (m_btm
.get_num_columns ());
1851 /* Now create child widgets. */
1853 if (flag_analyzer_debug_text_art
)
1855 table
t_headings (make_headings_table ());
1856 add_aligned_child_table (std::move (t_headings
));
1859 if (m_svalue_spatial_item
)
1861 table
t_sval (m_svalue_spatial_item
->make_table (m_btm
, m_sm
));
1862 add_aligned_child_table (std::move (t_sval
));
1867 (m_accessed_region_spatial_item
.make_table (m_btm
, m_sm
));
1868 add_aligned_child_table (std::move (t_accessed
));
1871 add_direction_widget ();
1873 table
t_valid (m_valid_region_spatial_item
.make_table (m_btm
, m_sm
));
1874 add_invalid_accesses_to_region_table (t_valid
);
1875 add_aligned_child_table (std::move (t_valid
));
1877 add_valid_vs_invalid_ruler ();
1880 const char *get_desc () const override
1882 return "access_diagram_impl";
1885 canvas::size_t calc_req_size () final override
1888 return canvas::size_t (0, 0);
1890 /* Now compute the size requirements for the tables. */
1891 for (auto iter
: m_aligned_table_widgets
)
1892 iter
->get_cell_sizes ().pass_1 (iter
->get_table ());
1893 for (auto iter
: m_aligned_table_widgets
)
1894 iter
->get_cell_sizes ().pass_2 (iter
->get_table ());
1898 /* ...and relayout the tables. */
1899 for (auto iter
: m_aligned_table_widgets
)
1900 iter
->recalc_coords ();
1902 /* Populate the canvas_x per table_x. */
1903 m_col_start_x
.clear ();
1904 int iter_canvas_x
= 0;
1905 for (auto w
: m_col_widths
->m_requirements
)
1907 m_col_start_x
.push_back (iter_canvas_x
);
1908 iter_canvas_x
+= w
+ 1;
1910 m_col_start_x
.push_back (iter_canvas_x
);
1912 m_calc_req_size_called
= true;
1914 return vbox_widget::calc_req_size ();
1917 int get_canvas_x_for_table_x (int table_x
) const
1919 gcc_assert (m_calc_req_size_called
);
1920 return m_col_start_x
[table_x
];
1923 canvas::range_t
get_canvas_x_range (const table::range_t
&table_x_range
) const
1925 gcc_assert (m_calc_req_size_called
);
1926 return canvas::range_t (get_canvas_x_for_table_x (table_x_range
.start
),
1927 get_canvas_x_for_table_x (table_x_range
.next
));
1930 const access_operation
&get_op () const { return m_op
; }
1932 style::id_t
get_style_id_for_validity (bool is_valid
) const
1934 return is_valid
? m_valid_style_id
: m_invalid_style_id
;
1937 const theme
&get_theme () const { return m_theme
; }
1940 /* Figure out all of the boundaries of interest when visualizing ths op. */
1941 std::unique_ptr
<boundaries
>
1942 find_boundaries () const
1944 std::unique_ptr
<boundaries
> result
1945 = make_unique
<boundaries
> (*m_op
.m_base_region
);
1947 m_valid_region_spatial_item
.add_boundaries (*result
, m_logger
);
1948 m_accessed_region_spatial_item
.add_boundaries (*result
, m_logger
);
1949 if (m_svalue_spatial_item
)
1950 m_svalue_spatial_item
->add_boundaries (*result
, m_logger
);
1955 void add_aligned_child_table (table t
)
1957 x_aligned_table_widget
*w
1958 = new x_aligned_table_widget (std::move (t
), m_theme
, *m_col_widths
);
1959 m_aligned_table_widgets
.push_back (w
);
1960 add_child (std::unique_ptr
<widget
> (w
));
1963 /* Create a table showing headings for use by -fanalyzer-debug-text-art, for
1965 +---------+-----------+-----------+---+--------------------------------+
1966 | tc0 | tc1 | tc2 |tc3| tc4 |
1967 +---------+-----------+-----------+---+--------------------------------+
1968 |bytes 0-3|bytes 4-35 |bytes 36-39| | bytes 40-43 |
1969 +---------+-----------+-----------+ +--------------------------------+
1971 - a row showing the table column numbers, labelled "tc0", "tc1", etc
1972 - a row showing the memory range of each table column that has one. */
1974 table
make_headings_table () const
1976 table
t (table::size_t (m_btm
.get_num_columns (), 2));
1978 for (int table_x
= 0; table_x
< t
.get_size ().w
; table_x
++)
1980 const int table_y
= 0;
1981 t
.set_cell (table::coord_t (table_x
, table_y
),
1982 fmt_styled_string (m_sm
, "tc%i", table_x
));
1984 for (int table_x
= 0; table_x
< t
.get_size ().w
; table_x
++)
1986 const int table_y
= 1;
1987 access_range
range_for_column (NULL
, bit_range (0, 0));
1988 if (m_btm
.maybe_get_access_range_for_table_x (table_x
,
1992 pp_format_decoder (&pp
) = default_tree_printer
;
1993 range_for_column
.dump_to_pp (&pp
, true);
1994 t
.set_cell (table::coord_t (table_x
, table_y
),
1995 styled_string (m_sm
, pp_formatted_text (&pp
)));
2002 void add_direction_widget ()
2004 add_child (::make_unique
<direction_widget
> (*this, m_btm
));
2007 void add_invalid_accesses_to_region_table (table
&t_region
)
2009 gcc_assert (t_region
.get_size ().w
== (int)m_btm
.get_num_columns ());
2011 const int table_y
= 0;
2012 const int table_h
= t_region
.get_size ().h
;
2014 access_range invalid_before_bits
;
2015 if (m_op
.maybe_get_invalid_before_bits (&invalid_before_bits
))
2017 t_region
.set_cell_span (m_btm
.get_table_rect (invalid_before_bits
,
2019 styled_string (m_sm
,
2020 _("before valid range")));
2022 access_range invalid_after_bits
;
2023 if (m_op
.maybe_get_invalid_after_bits (&invalid_after_bits
))
2025 t_region
.set_cell_span (m_btm
.get_table_rect (invalid_after_bits
,
2027 styled_string (m_sm
,
2028 _("after valid range")));
2032 void maybe_add_gap (x_aligned_x_ruler_widget
*w
,
2033 const access_range
&lower
,
2034 const access_range
&upper
) const
2036 LOG_SCOPE (m_logger
);
2039 lower
.log ("lower", *m_logger
);
2040 upper
.log ("upper", *m_logger
);
2042 tree lower_next
= lower
.m_next
.calc_symbolic_bit_offset (m_op
.m_model
);
2046 m_logger
->log ("failed to get lower_next");
2049 tree upper_start
= upper
.m_start
.calc_symbolic_bit_offset (m_op
.m_model
);
2053 m_logger
->log ("failed to get upper_start");
2056 tree num_bits_gap
= fold_build2 (MINUS_EXPR
,
2058 upper_start
, lower_next
);
2060 m_logger
->log ("num_bits_gap: %qE", num_bits_gap
);
2061 tree zero
= build_int_cst (size_type_node
, 0);
2062 tristate ts_gt_zero
= m_op
.m_model
.eval_condition (num_bits_gap
,
2066 if (ts_gt_zero
.is_false ())
2069 m_logger
->log ("rejecting as not > 0");
2073 bit_size_expr
num_bits (num_bits_gap
);
2074 styled_string label
= num_bits
.get_formatted_str (m_sm
,
2081 w
->add_range (m_btm
.get_table_x_for_range (access_range (lower
.m_next
,
2088 make_warning_string (styled_string
&&text
)
2090 styled_string result
;
2091 if (!m_theme
.emojis_p ())
2092 return std::move (text
);
2094 result
.append (styled_string (0x26A0, /* U+26A0 WARNING SIGN. */
2096 /* U+26A0 WARNING SIGN has East_Asian_Width == Neutral, but in its
2097 emoji variant is printed (by vte at least) with a 2nd half
2098 overlapping the next char. Hence we add two spaces here: a space
2099 to be covered by this overlap, plus another space of padding. */
2100 result
.append (styled_string (m_sm
, " "));
2101 result
.append (std::move (text
));
2105 /* Add a ruler child widet showing valid, invalid, and gaps. */
2106 void add_valid_vs_invalid_ruler ()
2108 LOG_SCOPE (m_logger
);
2110 x_aligned_x_ruler_widget
*w
2111 = new x_aligned_x_ruler_widget (*this, m_theme
, *m_col_widths
);
2113 access_range invalid_before_bits
;
2114 if (m_op
.maybe_get_invalid_before_bits (&invalid_before_bits
))
2117 invalid_before_bits
.log ("invalid_before_bits", *m_logger
);
2118 bit_size_expr num_before_bits
;
2119 if (invalid_before_bits
.get_size (m_op
.m_model
, &num_before_bits
))
2121 styled_string label
;
2122 if (m_op
.m_dir
== DIR_READ
)
2123 label
= num_before_bits
.get_formatted_str
2125 _("under-read of %wi bit"),
2126 _("under-read of %wi bits"),
2127 _("under-read of %wi byte"),
2128 _("under-read of %wi bytes"),
2129 _("under-read of %qE bits"),
2130 _("under-read of %qE bytes"));
2132 label
= num_before_bits
.get_formatted_str
2134 _("underwrite of %wi bit"),
2135 _("underwrite of %wi bits"),
2136 _("underwrite of %wi byte"),
2137 _("underwrite of %wi bytes"),
2138 _("underwrite of %qE bits"),
2139 _("underwrite of %qE bytes"));
2140 w
->add_range (m_btm
.get_table_x_for_range (invalid_before_bits
),
2141 make_warning_string (std::move (label
)),
2142 m_invalid_style_id
);
2148 m_logger
->log ("no invalid_before_bits");
2151 /* It would be nice to be able to use std::optional<access_range> here,
2152 but std::optional is C++17. */
2153 bool got_valid_bits
= false;
2154 access_range
valid_bits (m_op
.get_valid_bits ());
2155 bit_size_expr num_valid_bits
;
2156 if (valid_bits
.get_size (m_op
.m_model
, &num_valid_bits
))
2159 valid_bits
.log ("valid_bits", *m_logger
);
2161 got_valid_bits
= true;
2162 maybe_add_gap (w
, invalid_before_bits
, valid_bits
);
2164 styled_string label
;
2165 if (m_op
.m_dir
== DIR_READ
)
2166 label
= num_valid_bits
.get_formatted_str (m_sm
,
2168 _("size: %wi bits"),
2169 _("size: %wi byte"),
2170 _("size: %wi bytes"),
2171 _("size: %qE bits"),
2172 _("size: %qE bytes"));
2174 label
= num_valid_bits
.get_formatted_str (m_sm
,
2175 _("capacity: %wi bit"),
2176 _("capacity: %wi bits"),
2177 _("capacity: %wi byte"),
2178 _("capacity: %wi bytes"),
2179 _("capacity: %qE bits"),
2180 _("capacity: %qE bytes"));
2181 w
->add_range (m_btm
.get_table_x_for_range (m_op
.get_valid_bits ()),
2186 access_range invalid_after_bits
;
2187 if (m_op
.maybe_get_invalid_after_bits (&invalid_after_bits
))
2190 maybe_add_gap (w
, valid_bits
, invalid_after_bits
);
2193 invalid_before_bits
.log ("invalid_after_bits", *m_logger
);
2195 bit_size_expr num_after_bits
;
2196 if (invalid_after_bits
.get_size (m_op
.m_model
, &num_after_bits
))
2198 styled_string label
;
2199 if (m_op
.m_dir
== DIR_READ
)
2200 label
= num_after_bits
.get_formatted_str
2202 _("over-read of %wi bit"),
2203 _("over-read of %wi bits"),
2204 _("over-read of %wi byte"),
2205 _("over-read of %wi bytes"),
2206 _("over-read of %qE bits"),
2207 _("over-read of %qE bytes"));
2209 label
= num_after_bits
.get_formatted_str
2211 _("overflow of %wi bit"),
2212 _("overflow of %wi bits"),
2213 _("overflow of %wi byte"),
2214 _("overflow of %wi bytes"),
2215 _("over-read of %qE bits"),
2216 _("overflow of %qE bytes"));
2217 w
->add_range (m_btm
.get_table_x_for_range (invalid_after_bits
),
2218 make_warning_string (std::move (label
)),
2219 m_invalid_style_id
);
2225 m_logger
->log ("no invalid_after_bits");
2228 add_child (std::unique_ptr
<widget
> (w
));
2231 /* Subroutine of calc_req_size.
2232 Try to allocate surplus canvas width to table columns to make the
2233 per table-column canvas widths closer to being to scale.
2235 https://en.wikipedia.org/wiki/Fair_item_allocation
2236 https://en.wikipedia.org/wiki/Mathematics_of_apportionment
2238 void adjust_to_scale ()
2240 LOG_SCOPE (m_logger
);
2241 const unsigned num_columns
= m_btm
.get_num_columns ();
2242 std::vector
<bit_offset_t
> bit_sizes (num_columns
);
2243 for (unsigned table_x
= 0; table_x
< num_columns
; table_x
++)
2245 access_range
range_for_column (NULL
, bit_range (0, 0));
2246 if (m_btm
.maybe_get_access_range_for_table_x (table_x
,
2249 bit_size_t size_in_bits
;
2250 if (!range_for_column
.get_size_in_bits (&size_in_bits
))
2251 size_in_bits
= BITS_PER_UNIT
; // arbitrary non-zero value
2252 gcc_assert (size_in_bits
> 0);
2253 bit_sizes
[table_x
] = size_in_bits
;
2256 bit_sizes
[table_x
] = 0;
2259 while (adjust_to_scale_once (bit_sizes
))
2263 bool adjust_to_scale_once (const std::vector
<bit_offset_t
> &bit_sizes
)
2265 LOG_SCOPE (m_logger
);
2267 const unsigned num_columns
= m_btm
.get_num_columns ();
2269 /* Find the total canvas width currently required.
2270 Require one extra canvas column for the right-hand border
2272 int total_width
= 1;
2273 for (unsigned table_x
= 0; table_x
< num_columns
; table_x
++)
2275 int canvas_w
= m_col_widths
->m_requirements
[table_x
];
2276 gcc_assert (canvas_w
>= 0);
2277 total_width
+= canvas_w
+ 1;
2280 const int max_width
= param_analyzer_text_art_ideal_canvas_width
;
2281 if (total_width
>= max_width
)
2284 m_logger
->log ("bailing out: total_width=%i ,>= max_width (%i)\n",
2285 total_width
, max_width
);
2289 const int fixed_point
= 1024;
2290 std::vector
<bit_offset_t
> canvas_w_per_bit (num_columns
);
2291 for (unsigned table_x
= 0; table_x
< num_columns
; table_x
++)
2293 bit_offset_t bit_size
= bit_sizes
[table_x
];
2295 canvas_w_per_bit
[table_x
]
2296 = (m_col_widths
->m_requirements
[table_x
] * fixed_point
) / bit_size
;
2298 canvas_w_per_bit
[table_x
] = INT_MAX
;
2301 /* Find the min canvas per bit, and give an extra canvas column to
2302 the table column that has least. */
2303 size_t min_idx
= std::distance (canvas_w_per_bit
.begin (),
2304 std::min_element (canvas_w_per_bit
.begin (),
2305 canvas_w_per_bit
.end ()));
2306 m_col_widths
->m_requirements
[min_idx
] += 1;
2308 m_logger
->log ("adding 1 canvas_w to column %i\n", (int)min_idx
);
2310 return true; // keep going
2313 const access_operation
&m_op
;
2314 diagnostic_event_id_t m_region_creation_event_id
;
2315 style_manager
&m_sm
;
2316 const theme
&m_theme
;
2318 /* In lieu of being able to throw exceptions, a flag to mark this object
2322 style::id_t m_valid_style_id
;
2323 style::id_t m_invalid_style_id
;
2325 valid_region_spatial_item m_valid_region_spatial_item
;
2326 accessed_region_spatial_item m_accessed_region_spatial_item
;
2327 std::unique_ptr
<svalue_spatial_item
> m_svalue_spatial_item
;
2329 std::unique_ptr
<boundaries
> m_boundaries
;
2331 bit_to_table_map m_btm
;
2333 bool m_calc_req_size_called
;
2335 /* Column widths shared by all x_aligned_table_widget,
2336 created once we know how many columns we need. */
2337 std::unique_ptr
<table_dimension_sizes
> m_col_widths
;
2339 /* All of the child x_aligned_table_widget that share
2341 std::vector
<x_aligned_table_widget
*> m_aligned_table_widgets
;
2343 /* Mapping from table_x to canvas_x. */
2344 std::vector
<int> m_col_start_x
;
2348 x_aligned_x_ruler_widget::make_x_ruler () const
2350 x_ruler
r (x_ruler::label_dir::BELOW
);
2351 for (auto& iter
: m_labels
)
2353 canvas::range_t canvas_x_range
2354 = m_dia_impl
.get_canvas_x_range (iter
.m_table_x_range
);
2355 /* Include the end-point. */
2356 canvas_x_range
.next
++;
2357 r
.add_label (canvas_x_range
, iter
.m_text
.copy (), iter
.m_style_id
,
2358 x_ruler::label_kind::TEXT_WITH_BORDER
);
2363 /* class direction_widget : public leaf_widget. */
2365 /* Paint arrows indicating the direction of the access (read vs write),
2366 but only in the X-extent corresponding to the region that's actually
2370 direction_widget::paint_to_canvas (canvas
&canvas
)
2372 const access_range
accessed_bits (m_dia_impl
.get_op ().get_actual_bits ());
2374 const access_range
valid_bits (m_dia_impl
.get_op ().get_valid_bits ());
2376 for (unsigned table_x
= 0; table_x
< m_btm
.get_num_columns (); table_x
++)
2378 access_range column_access_range
;
2379 if (m_btm
.maybe_get_access_range_for_table_x (table_x
,
2380 &column_access_range
))
2382 /* Only paint arrows in the accessed region. */
2383 if (!accessed_bits
.contains_p (column_access_range
))
2386 /* Are we within the valid region? */
2387 const bool is_valid (valid_bits
.contains_p (column_access_range
));
2388 const style::id_t style_id
2389 = m_dia_impl
.get_style_id_for_validity (is_valid
);
2390 const canvas::range_t x_canvas_range
2391 = m_dia_impl
.get_canvas_x_range (table::range_t (table_x
,
2393 const int canvas_x
= x_canvas_range
.get_midpoint ();
2394 m_dia_impl
.get_theme ().paint_y_arrow
2397 canvas::range_t (get_y_range ()),
2398 (m_dia_impl
.get_op ().m_dir
== DIR_READ
2399 ? theme::y_arrow_dir::UP
2400 : theme::y_arrow_dir::DOWN
),
2406 /* class access_diagram : public text_art::wrapper_widget. */
2408 /* To hide the implementation details, this is merely a wrapper around
2409 an access_diagram_impl. */
2411 access_diagram::access_diagram (const access_operation
&op
,
2412 diagnostic_event_id_t region_creation_event_id
,
2416 : wrapper_widget (make_unique
<access_diagram_impl
> (op
,
2417 region_creation_event_id
,
2426 #endif /* #if ENABLE_ANALYZER */