1 /* Text art visualizations within -fanalyzer.
2 Copyright (C) 2023-2024 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-core.h"
34 #include "diagnostic.h"
36 #include "make-unique.h"
37 #include "tree-diagnostic.h" /* for default_tree_printer. */
38 #include "analyzer/analyzer.h"
39 #include "analyzer/region-model.h"
40 #include "analyzer/access-diagram.h"
41 #include "text-art/ruler.h"
42 #include "fold-const.h"
46 /* Consider this code:
49 where we've emitted a buffer overflow diagnostic like this:
50 out-of-bounds write from byte 40 till byte 43 but 'arr' ends at byte 40
52 We want to emit a diagram that visualizes:
53 - the spatial relationship between the valid region to access, versus
54 the region that was actually accessed: does it overlap, was it touching,
55 close, or far away? Was it before or after in memory? What are the
56 relative sizes involved?
57 - the direction of the access (read vs write)
59 The following code supports emitting diagrams similar to the following:
61 # +--------------------------------+
62 # |write from ‘x’ (type: ‘int32_t’)|
63 # +--------------------------------+
67 # +---------+-----------+-----------+ +--------------------------------+
68 # | [0] | ... | [9] | | after valid range |
69 # +---------+-----------+-----------+ | |
70 # | ‘arr’ (type: ‘int32_t[10]’) | | |
71 # +---------------------------------+ +--------------------------------+
72 # |~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~|
74 # +---------+--------+ +---------+---------+
75 # |capacity: 40 bytes| |overflow of 4 bytes|
76 # +------------------+ +-------------------+
78 where the diagram is laid out via table columns where each table column
79 represents either a range of bits/bytes, or is a spacing column (to highlight
80 the boundary between valid vs invalid accesses). The table columns can be
81 seen via -fanalyzer-debug-text-art. For example, here there are 5 table
82 columns ("tc0" through "tc4"):
84 # +---------+-----------+-----------+---+--------------------------------+
85 # | tc0 | tc1 | tc2 |tc3| tc4 |
86 # +---------+-----------+-----------+---+--------------------------------+
87 # |bytes 0-3|bytes 4-35 |bytes 36-39| | bytes 40-43 |
88 # +---------+-----------+-----------+ +--------------------------------+
90 # +--------------------------------+
91 # |write from ‘x’ (type: ‘int32_t’)|
92 # +--------------------------------+
96 # +---------+-----------+-----------+ +--------------------------------+
97 # | [0] | ... | [9] | | after valid range |
98 # +---------+-----------+-----------+ | |
99 # | ‘arr’ (type: ‘int32_t[10]’) | | |
100 # +---------------------------------+ +--------------------------------+
101 # |~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~|
103 # +---------+--------+ +---------+---------+
104 # |capacity: 40 bytes| |overflow of 4 bytes|
105 # +------------------+ +-------------------+
107 The diagram is built up from the following:
109 # +--------------------------------+
110 # | ITEM FOR SVALUE/ACCESSED REGION|
111 # +--------------------------------+
115 # +---------------------------------+ +--------------------------------+
116 # | VALID REGION | | INVALID ACCESS |
117 # +---------------------------------+ +--------------------------------+
119 # | VALID-VS-INVALID RULER |
121 i.e. a vbox_widget containing 4 child widgets laid out vertically:
122 - ALIGNED CHILD WIDGET: ITEM FOR SVALUE/ACCESSED REGION
124 - ALIGNED CHILD WIDGET: VALID AND INVALID ACCESSES
125 - VALID-VS-INVALID RULER.
127 A more complicated example, given this overflow:
129 strcpy (buf, LOREM_IPSUM);
131 01| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
132 02| |[0]|[1]|[2]|[3]|[4]|[5]| ... |[440]|[441]|[442]|[443]|[444]|[445]|
133 03| +---+---+---+---+---+---+ +-----+-----+-----+-----+-----+-----+
134 04| |'L'|'o'|'r'|'e'|'m'|' '| | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
135 05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
136 06| | string literal (type: 'char[446]') |
137 07| +----------------------------------------------------------------------+
138 08| | | | | | | | | | | | | | | |
139 09| | | | | | | | | | | | | | | |
140 10| v v v v v v v v v v v v v v v
141 11| +---+---------------------+----++--------------------------------------+
142 12| |[0]| ... |[99]|| after valid range |
143 13| +---+---------------------+----+| |
144 14| | 'buf' (type: 'char[100]') || |
145 15| +------------------------------++--------------------------------------+
146 16| |~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
148 18| +---------+---------+ +----------+----------+
149 19| |capacity: 100 bytes| |overflow of 346 bytes|
150 20| +-------------------+ +---------------------+
154 01| ALIGNED CHILD WIDGET (lines 01-07): (string_region_spatial_item)-+-----+
155 02| |[0]|[1]|[2]|[3]|[4]|[5]| ... |[440]|[441]|[442]|[443]|[444]|[445]|
156 03| +---+---+---+---+---+---+ +-----+-----+-----+-----+-----+-----+
157 04| |'L'|'o'|'r'|'e'|'m'|' '| | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
158 05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
159 06| | string literal (type: 'char[446]') |
160 07| +----------------------------------------------------------------------+
161 08| DIRECTION WIDGET (lines 08-10) | | | | | | |
162 09| | | | | | | | | | | | | | | |
163 10| v v v v v v v v v v v v v v v
164 11| ALIGNED CHILD WIDGET (lines 11-15)-------------------------------------+
165 12| VALID REGION ... |[99]|| INVALID ACCESS |
166 13| +---+---------------------+----+| |
167 14| | 'buf' (type: 'char[100]') || |
168 15| +------------------------------++--------------------------------------+
169 16| VALID-VS-INVALID RULER (lines 16-20): ~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
171 18| +---------+---------+ +----------+----------+
172 19| |capacity: 100 bytes| |overflow of 346 bytes|
173 20| +-------------------+ +---------------------+
175 We build the diagram in several phases:
176 - (1) we construct an access_diagram_impl widget. Within the ctor, we have
178 - (1.1) find all of the boundaries of interest
179 - (1.2) use the boundaries to build a bit_table_map, associating bit ranges
180 with table columns (e.g. "byte 0 is column 0, bytes 1-98 are column 2" etc)
181 - (1.3) create child widgets that share this table-based geometry
182 - (2) ask the widget for its size request
183 - (2.1) column widths and row heights for the table are computed by
184 access_diagram_impl::calc_req_size
185 - (2.2) child widgets request sizes based on these widths/heights
186 - (3) create a canvas of the appropriate size
187 - (4) paint the widget hierarchy to the canvas. */
190 using namespace text_art
;
195 fmt_styled_string (style_manager
&sm
,
196 const char *fmt
, ...)
197 ATTRIBUTE_GCC_DIAG(2, 3);
200 fmt_styled_string (style_manager
&sm
,
201 const char *fmt
, ...)
206 = styled_string::from_fmt_va (sm
, default_tree_printer
, fmt
, &ap
);
211 class access_diagram_impl
;
212 class bit_to_table_map
;
215 pp_bit_size_t (pretty_printer
*pp
, bit_size_t num_bits
)
217 if (num_bits
% BITS_PER_UNIT
== 0)
219 byte_size_t num_bytes
= num_bits
/ BITS_PER_UNIT
;
221 pp_printf (pp
, _("%wi byte"), num_bytes
.to_uhwi ());
223 pp_printf (pp
, _("%wi bytes"), num_bytes
.to_uhwi ());
228 pp_printf (pp
, _("%wi bit"), num_bits
.to_uhwi ());
230 pp_printf (pp
, _("%wi bits"), num_bits
.to_uhwi ());
235 get_access_size_str (style_manager
&sm
,
236 const access_operation
&op
,
237 access_range accessed_range
,
240 bit_size_expr num_bits
;
241 if (accessed_range
.get_size (op
.m_model
, &num_bits
))
248 num_bits
.print (&pp
);
250 if (op
.m_dir
== DIR_READ
)
251 return fmt_styled_string (sm
,
252 _("read of %qT (%s)"),
254 pp_formatted_text (&pp
));
256 return fmt_styled_string (sm
,
257 _("write of %qT (%s)"),
259 pp_formatted_text (&pp
));
261 if (op
.m_dir
== DIR_READ
)
262 return num_bits
.get_formatted_str (sm
,
263 _("read of %wi bit"),
264 _("read of %wi bits"),
265 _("read of %wi byte"),
266 _("read of %wi bytes"),
267 _("read of %qE bits"),
268 _("read of %qE bytes"));
270 return num_bits
.get_formatted_str (sm
,
271 _("write of %wi bit"),
272 _("write of %wi bits"),
273 _("write of %wi byte"),
274 _("write of %wi bytes"),
275 _("write of %qE bits"),
276 _("write of %qE bytes"));
281 if (op
.m_dir
== DIR_READ
)
282 return fmt_styled_string (sm
, _("read of %qT"), type
);
284 return fmt_styled_string (sm
, _("write of %qT"), type
);
287 if (op
.m_dir
== DIR_READ
)
288 return styled_string (sm
, _("read"));
290 return styled_string (sm
, _("write"));
293 /* Subroutine of clean_up_for_diagram. */
296 strip_any_cast (tree expr
)
298 if (TREE_CODE (expr
) == NOP_EXPR
299 || TREE_CODE (expr
) == NON_LVALUE_EXPR
)
300 expr
= TREE_OPERAND (expr
, 0);
304 /* Subroutine of clean_up_for_diagram. */
307 remove_ssa_names (tree expr
)
309 if (TREE_CODE (expr
) == SSA_NAME
310 && SSA_NAME_VAR (expr
))
311 return SSA_NAME_VAR (expr
);
312 tree t
= copy_node (expr
);
313 for (int i
= 0; i
< TREE_OPERAND_LENGTH (expr
); i
++)
314 TREE_OPERAND (t
, i
) = remove_ssa_names (TREE_OPERAND (expr
, i
));
318 /* We want to be able to print tree expressions from the analyzer,
319 which is in the middle end.
321 We could use the front-end pretty_printer's formatting routine,
323 (a) some have additional state in a pretty_printer subclass, so we'd
324 need to clone global_dc->printer
325 (b) the "aka" type information added by the C and C++ frontends are
326 too verbose when building a diagram, and there isn't a good way to ask
327 for a less verbose version of them.
329 Hence we use default_tree_printer.
330 However, we want to avoid printing SSA names, and instead print the
332 Ideally there would be a better tree printer for use by middle end
333 warnings, but as workaround, this function clones a tree, replacing
334 SSA names with the var names. */
337 clean_up_for_diagram (tree expr
)
339 tree without_ssa_names
= remove_ssa_names (expr
);
340 return strip_any_cast (without_ssa_names
);
343 /* struct bit_size_expr. */
345 text_art::styled_string
346 bit_size_expr::get_formatted_str (text_art::style_manager
&sm
,
347 const char *concrete_single_bit_fmt
,
348 const char *concrete_plural_bits_fmt
,
349 const char *concrete_single_byte_fmt
,
350 const char *concrete_plural_bytes_fmt
,
351 const char *symbolic_bits_fmt
,
352 const char *symbolic_bytes_fmt
) const
354 if (TREE_CODE (m_num_bits
) == INTEGER_CST
)
356 bit_size_t concrete_num_bits
= wi::to_offset (m_num_bits
);
357 if (concrete_num_bits
% BITS_PER_UNIT
== 0)
359 byte_size_t concrete_num_bytes
= concrete_num_bits
/ BITS_PER_UNIT
;
360 if (concrete_num_bytes
== 1)
361 return fmt_styled_string (sm
, concrete_single_byte_fmt
,
362 concrete_num_bytes
.to_uhwi ());
364 return fmt_styled_string (sm
, concrete_plural_bytes_fmt
,
365 concrete_num_bytes
.to_uhwi ());
369 if (concrete_num_bits
== 1)
370 return fmt_styled_string (sm
, concrete_single_bit_fmt
,
371 concrete_num_bits
.to_uhwi ());
373 return fmt_styled_string (sm
, concrete_plural_bits_fmt
,
374 concrete_num_bits
.to_uhwi ());
379 if (tree bytes_expr
= maybe_get_as_bytes ())
380 return fmt_styled_string (sm
,
382 clean_up_for_diagram (bytes_expr
));
383 return fmt_styled_string (sm
,
385 clean_up_for_diagram (m_num_bits
));
390 bit_size_expr::print (pretty_printer
*pp
) const
392 if (TREE_CODE (m_num_bits
) == INTEGER_CST
)
394 bit_size_t concrete_num_bits
= wi::to_offset (m_num_bits
);
395 pp_bit_size_t (pp
, concrete_num_bits
);
399 if (tree bytes_expr
= maybe_get_as_bytes ())
400 pp_printf (pp
, _("%qE bytes"), bytes_expr
);
402 pp_printf (pp
, _("%qE bits"), m_num_bits
);
407 bit_size_expr::maybe_get_as_bytes () const
409 switch (TREE_CODE (m_num_bits
))
415 const bit_size_t num_bits
= wi::to_offset (m_num_bits
);
416 if (num_bits
% BITS_PER_UNIT
!= 0)
418 const bit_size_t num_bytes
= num_bits
/ BITS_PER_UNIT
;
419 return wide_int_to_tree (size_type_node
, num_bytes
);
426 = bit_size_expr (TREE_OPERAND (m_num_bits
, 0));
427 tree op0_as_bytes
= op0
.maybe_get_as_bytes ();
431 = bit_size_expr (TREE_OPERAND (m_num_bits
, 1));
432 tree op1_as_bytes
= op1
.maybe_get_as_bytes ();
435 return fold_build2 (TREE_CODE (m_num_bits
), size_type_node
,
436 op0_as_bytes
, op1_as_bytes
);
442 = bit_size_expr (TREE_OPERAND (m_num_bits
, 1));
443 if (tree op1_as_bytes
= op1
.maybe_get_as_bytes ())
444 return fold_build2 (MULT_EXPR
, size_type_node
,
445 TREE_OPERAND (m_num_bits
, 0),
453 /* struct access_range. */
455 access_range::access_range (const region
*base_region
, const bit_range
&bits
)
456 : m_start (region_offset::make_concrete (base_region
,
457 bits
.get_start_bit_offset ())),
458 m_next (region_offset::make_concrete (base_region
,
459 bits
.get_next_bit_offset ()))
463 access_range::access_range (const region
*base_region
, const byte_range
&bytes
)
464 : m_start (region_offset::make_concrete (base_region
,
465 bytes
.get_start_bit_offset ())),
466 m_next (region_offset::make_concrete (base_region
,
467 bytes
.get_next_bit_offset ()))
471 access_range::access_range (const region
®
, region_model_manager
*mgr
)
472 : m_start (reg
.get_offset (mgr
)),
473 m_next (reg
.get_next_offset (mgr
))
478 access_range::get_size (const region_model
&model
, bit_size_expr
*out
) const
480 tree start_expr
= m_start
.calc_symbolic_bit_offset (model
);
483 tree next_expr
= m_next
.calc_symbolic_bit_offset (model
);
486 *out
= bit_size_expr (fold_build2 (MINUS_EXPR
, size_type_node
,
487 next_expr
, start_expr
));
492 access_range::contains_p (const access_range
&other
) const
494 return (m_start
<= other
.m_start
495 && other
.m_next
<= m_next
);
499 access_range::empty_p () const
501 bit_range
concrete_bits (0, 0);
502 if (!as_concrete_bit_range (&concrete_bits
))
504 return concrete_bits
.empty_p ();
508 access_range::dump_to_pp (pretty_printer
*pp
, bool simple
) const
510 if (m_start
.concrete_p () && m_next
.concrete_p ())
512 bit_range
bits (m_start
.get_bit_offset (),
513 m_next
.get_bit_offset () - m_start
.get_bit_offset ());
514 bits
.dump_to_pp (pp
);
517 pp_character (pp
, '[');
518 m_start
.dump_to_pp (pp
, simple
);
519 pp_string (pp
, " to ");
520 m_next
.dump_to_pp (pp
, simple
);
521 pp_character (pp
, ')');
525 access_range::dump (bool simple
) const
528 pp_format_decoder (&pp
) = default_tree_printer
;
529 pp_show_color (&pp
) = pp_show_color (global_dc
->printer
);
530 pp
.buffer
->stream
= stderr
;
531 dump_to_pp (&pp
, simple
);
537 access_range::log (const char *title
, logger
&logger
) const
539 logger
.start_log_line ();
540 logger
.log_partial ("%s: ", title
);
541 dump_to_pp (logger
.get_printer (), true);
542 logger
.end_log_line ();
545 /* struct access_operation. */
548 access_operation::get_valid_bits () const
550 const svalue
*capacity_in_bytes_sval
= m_model
.get_capacity (m_base_region
);
552 (region_offset::make_concrete (m_base_region
, 0),
553 region_offset::make_byte_offset (m_base_region
, capacity_in_bytes_sval
));
557 access_operation::get_actual_bits () const
559 return access_range (m_reg
, get_manager ());
562 /* If there are any bits accessed invalidly before the valid range,
563 return true and write their range to *OUT.
564 Return false if there aren't, or if there's a problem
565 (e.g. symbolic ranges. */
568 access_operation::maybe_get_invalid_before_bits (access_range
*out
) const
570 access_range
valid_bits (get_valid_bits ());
571 access_range
actual_bits (get_actual_bits ());
573 if (actual_bits
.m_start
>= valid_bits
.m_start
)
575 /* No part of accessed range is before the valid range. */
578 else if (actual_bits
.m_next
> valid_bits
.m_start
)
580 /* Get part of accessed range that's before the valid range. */
581 *out
= access_range (actual_bits
.m_start
, valid_bits
.m_start
);
586 /* Accessed range is fully before valid range. */
592 /* If there are any bits accessed invalidly after the valid range,
593 return true and write their range to *OUT.
594 Return false if there aren't, or if there's a problem. */
597 access_operation::maybe_get_invalid_after_bits (access_range
*out
) const
599 access_range
valid_bits (get_valid_bits ());
600 access_range
actual_bits (get_actual_bits ());
602 if (actual_bits
.m_next
<= valid_bits
.m_next
)
604 /* No part of accessed range is after the valid range. */
607 else if (actual_bits
.m_start
< valid_bits
.m_next
)
609 /* Get part of accessed range that's after the valid range. */
610 *out
= access_range (valid_bits
.m_next
, actual_bits
.m_next
);
615 /* Accessed range is fully after valid range. */
621 /* A class for capturing all of the region offsets of interest (both concrete
622 and symbolic), to help align everything in the diagram.
623 Boundaries can be soft or hard; hard boundaries are emphasized visually
624 (e.g. the boundary between valid vs invalid accesses).
626 Offsets in the boundaries are all expressed relative to the base
627 region of the access_operation. */
632 enum class kind
{ HARD
, SOFT
};
634 boundaries (const region
&base_reg
, logger
*logger
)
635 : m_base_reg (base_reg
), m_logger (logger
)
639 void add (region_offset offset
, enum kind k
)
641 m_all_offsets
.insert (offset
);
643 m_hard_offsets
.insert (offset
);
646 void add (const access_range
&range
, enum kind kind
)
648 add (range
.m_start
, kind
);
649 add (range
.m_next
, kind
);
652 m_logger
->start_log_line ();
653 m_logger
->log_partial ("added access_range: ");
654 range
.dump_to_pp (m_logger
->get_printer (), true);
655 m_logger
->log_partial (" (%s)",
656 (kind
== boundaries::kind::HARD
)
658 m_logger
->end_log_line ();
662 void add (const region
®
, region_model_manager
*mgr
, enum kind kind
)
664 add (access_range (reg
.get_offset (mgr
),
665 reg
.get_next_offset (mgr
)),
669 void add (const byte_range bytes
, enum kind kind
)
671 add (access_range (&m_base_reg
, bytes
), kind
);
674 void add_all_bytes_in_range (const byte_range
&bytes
)
676 for (byte_offset_t byte_idx
= bytes
.get_start_byte_offset ();
677 byte_idx
<= bytes
.get_next_byte_offset ();
678 byte_idx
= byte_idx
+ 1)
679 add (region_offset::make_concrete (&m_base_reg
, byte_idx
* 8),
683 void add_all_bytes_in_range (const access_range
&range
)
685 byte_range
bytes (0, 0);
686 bool valid
= range
.as_concrete_byte_range (&bytes
);
688 add_all_bytes_in_range (bytes
);
691 void log (logger
&logger
) const
693 logger
.log ("boundaries:");
694 logger
.inc_indent ();
695 for (auto offset
: m_all_offsets
)
697 enum kind k
= get_kind (offset
);
698 logger
.start_log_line ();
699 logger
.log_partial ("%s: ", (k
== kind::HARD
) ? "HARD" : "soft");
700 offset
.dump_to_pp (logger
.get_printer (), true);
701 logger
.end_log_line ();
703 logger
.dec_indent ();
706 enum kind
get_kind (region_offset offset
) const
708 gcc_assert (m_all_offsets
.find (offset
) != m_all_offsets
.end ());
709 if (m_hard_offsets
.find (offset
) != m_hard_offsets
.end ())
715 std::set
<region_offset
>::const_iterator
begin () const
717 return m_all_offsets
.begin ();
719 std::set
<region_offset
>::const_iterator
end () const
721 return m_all_offsets
.end ();
723 std::set
<region_offset
>::size_type
size () const
725 return m_all_offsets
.size ();
728 std::vector
<region_offset
>
729 get_hard_boundaries_in_range (byte_offset_t min_offset
,
730 byte_offset_t max_offset
) const
732 std::vector
<region_offset
> result
;
733 for (auto &offset
: m_hard_offsets
)
735 if (!offset
.concrete_p ())
738 if (!offset
.get_concrete_byte_offset (&byte
))
740 if (byte
< min_offset
)
742 if (byte
> max_offset
)
744 result
.push_back (offset
);
750 const region
&m_base_reg
;
752 std::set
<region_offset
> m_all_offsets
;
753 std::set
<region_offset
> m_hard_offsets
;
756 /* A widget that wraps a table but offloads column-width calculation
757 to a shared object, so that we can vertically line up multiple tables
758 and have them all align their columns.
762 01| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
763 02| |[0]|[1]|[2]|[3]|[4]|[5]| ... |[440]|[441]|[442]|[443]|[444]|[445]|
764 03| +---+---+---+---+---+---+ +-----+-----+-----+-----+-----+-----+
765 04| |'L'|'o'|'r'|'e'|'m'|' '| | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
766 05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
767 06| | string literal (type: 'char[446]') |
768 07| +----------------------------------------------------------------------+
769 08| | | | | | | | | | | | | | | |
770 09| | | | | | | | | | | | | | | |
771 10| v v v v v v v v v v v v v v v
772 11|+---+---------------------+----++--------------------------------------+
773 12||[0]| ... |[99]|| after valid range |
774 13|+---+---------------------+----+| |
775 14|| 'buf' (type: 'char[100]') || |
776 15|+------------------------------++--------------------------------------+
777 16||~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
779 18| +---------+---------+ +----------+----------+
780 19| |capacity: 100 bytes| |overflow of 346 bytes|
781 20| +-------------------+ +---------------------+
783 rows 01-07 and rows 11-15 are x_aligned_table_widget instances. */
785 class x_aligned_table_widget
: public leaf_widget
788 x_aligned_table_widget (table t
,
790 table_dimension_sizes
&col_widths
)
791 : m_table (std::move (t
)),
793 m_col_widths (col_widths
),
794 m_row_heights (t
.get_size ().h
),
795 m_cell_sizes (m_col_widths
, m_row_heights
),
796 m_tg (m_table
, m_cell_sizes
)
800 const char *get_desc () const override
802 return "x_aligned_table_widget";
805 canvas::size_t calc_req_size () final override
807 /* We don't compute the size requirements;
808 the parent should have done this. */
809 return m_tg
.get_canvas_size ();
812 void paint_to_canvas (canvas
&canvas
) final override
814 m_table
.paint_to_canvas (canvas
,
820 const table
&get_table () const { return m_table
; }
821 table_cell_sizes
&get_cell_sizes () { return m_cell_sizes
; }
822 void recalc_coords ()
824 m_tg
.recalc_coords ();
829 const theme
&m_theme
;
830 table_dimension_sizes
&m_col_widths
; // Reference to shared column widths
831 table_dimension_sizes m_row_heights
; // Unique row heights
832 table_cell_sizes m_cell_sizes
;
836 /* A widget for printing arrows between the accessed region
837 and the svalue, showing the direction of the access.
841 01| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
842 02| |[0]|[1]|[2]|[3]|[4]|[5]| ... |[440]|[441]|[442]|[443]|[444]|[445]|
843 03| +---+---+---+---+---+---+ +-----+-----+-----+-----+-----+-----+
844 04| |'L'|'o'|'r'|'e'|'m'|' '| | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
845 05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
846 06| | string literal (type: 'char[446]') |
847 07| +----------------------------------------------------------------------+
848 08| | | | | | | | | | | | | | | |
849 09| | | | | | | | | | | | | | | |
850 10| v v v v v v v v v v v v v v v
851 11|+---+---------------------+----++--------------------------------------+
852 12||[0]| ... |[99]|| after valid range |
853 13|+---+---------------------+----+| |
854 14|| 'buf' (type: 'char[100]') || |
855 15|+------------------------------++--------------------------------------+
856 16||~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
858 18| +---------+---------+ +----------+----------+
859 19| |capacity: 100 bytes| |overflow of 346 bytes|
860 20| +-------------------+ +---------------------+
862 rows 8-10 are the direction widget. */
864 class direction_widget
: public leaf_widget
867 direction_widget (const access_diagram_impl
&dia_impl
,
868 const bit_to_table_map
&btm
)
870 m_dia_impl (dia_impl
),
874 const char *get_desc () const override
876 return "direction_widget";
878 canvas::size_t calc_req_size () final override
880 /* Get our width from our siblings. */
881 return canvas::size_t (0, 3);
883 void paint_to_canvas (canvas
&canvas
) final override
;
886 const access_diagram_impl
&m_dia_impl
;
887 const bit_to_table_map
&m_btm
;
890 /* A widget for adding an x_ruler to a diagram based on table columns,
891 offloading column-width calculation to shared objects, so that the ruler
892 lines up with other tables in the diagram.
896 01| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
897 02| |[0]|[1]|[2]|[3]|[4]|[5]| ... |[440]|[441]|[442]|[443]|[444]|[445]|
898 03| +---+---+---+---+---+---+ +-----+-----+-----+-----+-----+-----+
899 04| |'L'|'o'|'r'|'e'|'m'|' '| | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
900 05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
901 06| | string literal (type: 'char[446]') |
902 07| +----------------------------------------------------------------------+
903 08| | | | | | | | | | | | | | | |
904 09| | | | | | | | | | | | | | | |
905 10| v v v v v v v v v v v v v v v
906 11|+---+---------------------+----++--------------------------------------+
907 12||[0]| ... |[99]|| after valid range |
908 13|+---+---------------------+----+| |
909 14|| 'buf' (type: 'char[100]') || |
910 15|+------------------------------++--------------------------------------+
911 16||~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
913 18| +---------+---------+ +----------+----------+
914 19| |capacity: 100 bytes| |overflow of 346 bytes|
915 20| +-------------------+ +---------------------+
917 rows 16-20 are the x_aligned_x_ruler_widget. */
919 class x_aligned_x_ruler_widget
: public leaf_widget
922 x_aligned_x_ruler_widget (const access_diagram_impl
&dia_impl
,
924 : m_dia_impl (dia_impl
),
929 const char *get_desc () const override
931 return "x_aligned_ruler_widget";
934 void add_range (const table::range_t
&x_range
,
936 style::id_t style_id
)
938 m_labels
.push_back (label (x_range
, std::move (text
), style_id
));
941 canvas::size_t calc_req_size () final override
943 x_ruler
r (make_x_ruler ());
944 return r
.get_size ();
947 void paint_to_canvas (canvas
&canvas
) final override
949 x_ruler
r (make_x_ruler ());
950 r
.paint_to_canvas (canvas
,
958 label (const table::range_t
&table_x_range
,
960 style::id_t style_id
)
961 : m_table_x_range (table_x_range
),
962 m_text (std::move (text
)),
963 m_style_id (style_id
)
966 table::range_t m_table_x_range
;
967 styled_string m_text
;
968 style::id_t m_style_id
;
971 x_ruler
make_x_ruler () const;
973 const access_diagram_impl
&m_dia_impl
;
974 const theme
&m_theme
;
975 std::vector
<label
> m_labels
;
978 /* A two-way mapping between access_ranges and table columns, for use by
979 spatial_item subclasses for creating tables.
980 For example when visualizing a bogus access of 'int arr[10];'
981 at 'arr[10]', we might have:
982 - table column 0 is "bytes 0-3" (for arr[0])
983 - table column 1 is "bytes 4-35" (for arr[1] through arr[8])
984 - table column 2 is "bytes 36-39 (for arr[9])
985 - table column 3 is blank to emphasize a hard boundary between
986 valid/invalid accesses.
987 - table column 4 is "bytes 40-44" (for arr[10])
989 We store this as a pair of maps from region_offset to table x; in
992 region offset table_x prev_table_x
993 bit 0 (aka byte 0) 0 (none)
994 bit 32 (aka byte 4) 1 0
995 bit 288 (aka byte 36) 2 1
996 bit 320 (aka byte 40) 4 2
997 bit 352 (aka byte 44) (none) (none)
999 so that e.g given the half-open byte range [0, 40)
1000 we can determine the closed range of table x [0, 2]. */
1002 class bit_to_table_map
1005 /* Populate m_table_x_for_bit and m_bit_for_table_x. */
1006 void populate (const boundaries
&boundaries
, logger
*logger
)
1011 std::vector
<region_offset
> vec_boundaries (boundaries
.begin (),
1014 /* Sort into an order that makes sense. */
1015 std::sort (vec_boundaries
.begin (),
1016 vec_boundaries
.end ());
1020 logger
->log ("vec_boundaries");
1021 logger
->inc_indent ();
1022 for (unsigned idx
= 0; idx
< vec_boundaries
.size (); idx
++)
1024 logger
->start_log_line ();
1025 logger
->log_partial ("idx: %i: ", idx
);
1026 vec_boundaries
[idx
].dump_to_pp (logger
->get_printer (), true);
1027 logger
->end_log_line ();
1029 logger
->dec_indent ();
1032 for (size_t idx
= 0; idx
< vec_boundaries
.size (); idx
++)
1034 const region_offset
&offset
= vec_boundaries
[idx
];
1035 if (idx
> 0 && (idx
+ 1) < vec_boundaries
.size ())
1037 if (boundaries
.get_kind (offset
) == boundaries::kind::HARD
)
1040 m_table_x_for_offset
[offset
] = table_x
;
1041 if ((idx
+ 1) < vec_boundaries
.size ())
1043 const region_offset
&next_offset
= vec_boundaries
[idx
+ 1];
1044 m_table_x_for_prev_offset
[next_offset
] = table_x
;
1045 m_range_for_table_x
[table_x
] = access_range (offset
, next_offset
);
1049 m_num_columns
= table_x
- 1;
1055 unsigned get_num_columns () const
1057 return m_num_columns
;
1060 table::range_t
get_table_x_for_range (const access_range
&range
) const
1062 return table::range_t (get_table_x_for_offset (range
.m_start
),
1063 get_table_x_for_prev_offset (range
.m_next
) + 1);
1066 table::rect_t
get_table_rect (const access_range
&range
,
1067 const int table_y
, const int table_h
) const
1069 const table::range_t
x_range (get_table_x_for_range (range
));
1070 return table::rect_t (table::coord_t (x_range
.start
, table_y
),
1071 table::size_t (x_range
.get_size (), table_h
));
1074 table::rect_t
get_table_rect (const region
*base_reg
,
1075 const bit_range
&bits
,
1076 const int table_y
, const int table_h
) const
1078 const access_range
range (base_reg
, bits
);
1079 return get_table_rect (range
, table_y
, table_h
);
1082 table::rect_t
get_table_rect (const region
*base_reg
,
1083 const byte_range
&bytes
,
1084 const int table_y
, const int table_h
) const
1086 return get_table_rect (base_reg
, bytes
.as_bit_range (), table_y
, table_h
);
1089 bool maybe_get_access_range_for_table_x (int table_x
,
1090 access_range
*out
) const
1092 auto slot
= m_range_for_table_x
.find (table_x
);
1093 if (slot
== m_range_for_table_x
.end ())
1095 *out
= slot
->second
;
1099 void log (logger
&logger
) const
1101 logger
.log ("table columns");
1102 logger
.inc_indent ();
1103 for (unsigned table_x
= 0; table_x
< get_num_columns (); table_x
++)
1105 logger
.start_log_line ();
1106 logger
.log_partial ("table_x: %i", table_x
);
1107 access_range
range_for_column (NULL
, bit_range (0, 0));
1108 if (maybe_get_access_range_for_table_x (table_x
, &range_for_column
))
1110 logger
.log_partial (": range: ");
1111 range_for_column
.dump_to_pp (logger
.get_printer (), true);
1113 logger
.end_log_line ();
1115 logger
.dec_indent ();
1118 int get_table_x_for_offset (region_offset offset
) const
1120 auto slot
= m_table_x_for_offset
.find (offset
);
1122 /* If this fails, then we probably failed to fully populate m_boundaries
1123 in find_boundaries. */
1124 gcc_assert (slot
!= m_table_x_for_offset
.end ());
1126 return slot
->second
;
1130 int get_table_x_for_prev_offset (region_offset offset
) const
1132 auto slot
= m_table_x_for_prev_offset
.find (offset
);
1134 /* If this fails, then we probably failed to fully populate m_boundaries
1135 in find_boundaries. */
1136 gcc_assert (slot
!= m_table_x_for_prev_offset
.end ());
1138 return slot
->second
;
1141 std::map
<region_offset
, int> m_table_x_for_offset
;
1142 std::map
<region_offset
, int> m_table_x_for_prev_offset
;
1143 std::map
<int, access_range
> m_range_for_table_x
;
1144 unsigned m_num_columns
;
1147 /* Base class for something in the diagram that participates
1148 in two steps of diagram creation:
1149 (a) populating a boundaries instance with the boundaries of interest
1150 (b) creating a table instance for itself.
1152 Offsets in the boundaries are all expressed relative to the base
1153 region of the access_operation. */
1158 virtual ~spatial_item () {}
1159 virtual void add_boundaries (boundaries
&out
, logger
*) const = 0;
1161 virtual table
make_table (const bit_to_table_map
&btm
,
1162 style_manager
&sm
) const = 0;
1165 /* A spatial_item that involves showing an svalue at a particular offset. */
1167 class svalue_spatial_item
: public spatial_item
1176 svalue_spatial_item (const svalue
&sval
,
1179 : m_sval (sval
), m_bits (bits
), m_kind (kind
)
1183 const svalue
&m_sval
;
1184 access_range m_bits
;
1188 static std::unique_ptr
<spatial_item
>
1189 make_existing_svalue_spatial_item (const svalue
*sval
,
1190 const access_range
&bits
,
1191 const theme
&theme
);
1193 class compound_svalue_spatial_item
: public svalue_spatial_item
1196 compound_svalue_spatial_item (const compound_svalue
&sval
,
1197 const access_range
&bits
,
1200 : svalue_spatial_item (sval
, bits
, kind
),
1201 m_compound_sval (sval
)
1203 const binding_map
&map
= m_compound_sval
.get_map ();
1204 auto_vec
<const binding_key
*> binding_keys
;
1205 for (auto iter
: map
)
1207 const binding_key
*key
= iter
.first
;
1208 const svalue
*bound_sval
= iter
.second
;
1209 if (const concrete_binding
*concrete_key
1210 = key
->dyn_cast_concrete_binding ())
1212 access_range
range (nullptr,
1213 concrete_key
->get_bit_range ());
1214 if (std::unique_ptr
<spatial_item
> child
1215 = make_existing_svalue_spatial_item (bound_sval
,
1218 m_children
.push_back (std::move (child
));
1223 void add_boundaries (boundaries
&out
, logger
*logger
) const final override
1226 for (auto &iter
: m_children
)
1227 iter
->add_boundaries (out
, logger
);
1230 table
make_table (const bit_to_table_map
&btm
,
1231 style_manager
&sm
) const final override
1233 std::vector
<table
> child_tables
;
1235 for (auto &iter
: m_children
)
1237 table
child_table (iter
->make_table (btm
, sm
));
1238 max_rows
= MAX (max_rows
, child_table
.get_size ().h
);
1239 child_tables
.push_back (std::move (child_table
));
1241 table
t (table::size_t (btm
.get_num_columns (), max_rows
));
1242 for (auto &&child_table
: child_tables
)
1243 t
.add_other_table (std::move (child_table
),
1244 table::coord_t (0, 0));
1249 const compound_svalue
&m_compound_sval
;
1250 std::vector
<std::unique_ptr
<spatial_item
>> m_children
;
1253 /* Loop through the TABLE_X_RANGE columns of T, adding
1254 cells containing "..." in any unoccupied ranges of table cell. */
1257 add_ellipsis_to_gaps (table
&t
,
1259 const table::range_t
&table_x_range
,
1260 const table::range_t
&table_y_range
)
1262 int table_x
= table_x_range
.get_min ();
1263 while (table_x
< table_x_range
.get_next ())
1265 /* Find a run of unoccupied table cells. */
1266 const int start_table_x
= table_x
;
1267 while (table_x
< table_x_range
.get_next ()
1268 && !t
.get_placement_at (table::coord_t (table_x
,
1269 table_y_range
.get_min ())))
1271 const table::range_t
unoccupied_x_range (start_table_x
, table_x
);
1272 if (unoccupied_x_range
.get_size () > 0)
1273 t
.set_cell_span (table::rect_t (unoccupied_x_range
, table_y_range
),
1274 styled_string (sm
, "..."));
1275 /* Skip occupied table cells. */
1276 while (table_x
< table_x_range
.get_next ()
1277 && t
.get_placement_at (table::coord_t (table_x
,
1278 table_y_range
.get_min ())))
1283 /* Subclass of spatial_item for visualizing the region of memory
1284 that's valid to access relative to the base region of region accessed in
1287 class valid_region_spatial_item
: public spatial_item
1290 valid_region_spatial_item (const access_operation
&op
,
1291 diagnostic_event_id_t region_creation_event_id
,
1294 m_region_creation_event_id (region_creation_event_id
),
1295 m_boundaries (nullptr),
1296 m_existing_sval (op
.m_model
.get_store_value (op
.m_base_region
, nullptr)),
1297 m_existing_sval_spatial_item
1298 (make_existing_svalue_spatial_item (m_existing_sval
,
1299 op
.get_valid_bits (),
1304 void add_boundaries (boundaries
&out
, logger
*logger
) const final override
1307 m_boundaries
= &out
;
1308 access_range valid_bits
= m_op
.get_valid_bits ();
1311 logger
->start_log_line ();
1312 logger
->log_partial ("valid bits: ");
1313 valid_bits
.dump_to_pp (logger
->get_printer (), true);
1314 logger
->end_log_line ();
1316 out
.add (valid_bits
, boundaries::kind::HARD
);
1318 if (m_existing_sval_spatial_item
)
1322 logger
->start_log_line ();
1323 logger
->log_partial ("existing svalue: ");
1324 m_existing_sval
->dump_to_pp (logger
->get_printer (), true);
1325 logger
->end_log_line ();
1327 m_existing_sval_spatial_item
->add_boundaries (out
, logger
);
1330 /* Support for showing first and final element in array types. */
1331 if (tree base_type
= m_op
.m_base_region
->get_type ())
1332 if (TREE_CODE (base_type
) == ARRAY_TYPE
)
1335 logger
->log ("showing first and final element in array type");
1336 region_model_manager
*mgr
= m_op
.m_model
.get_manager ();
1337 tree domain
= TYPE_DOMAIN (base_type
);
1338 if (domain
&& TYPE_MIN_VALUE (domain
) && TYPE_MAX_VALUE (domain
))
1340 const svalue
*min_idx_sval
1341 = mgr
->get_or_create_constant_svalue (TYPE_MIN_VALUE (domain
));
1342 const svalue
*max_idx_sval
1343 = mgr
->get_or_create_constant_svalue (TYPE_MAX_VALUE (domain
));
1344 const region
*min_element
=
1345 mgr
->get_element_region (m_op
.m_base_region
,
1346 TREE_TYPE (base_type
),
1348 out
.add (*min_element
, mgr
, boundaries::kind::SOFT
);
1349 const region
*max_element
=
1350 mgr
->get_element_region (m_op
.m_base_region
,
1351 TREE_TYPE (base_type
),
1353 out
.add (*max_element
, mgr
, boundaries::kind::SOFT
);
1358 /* Subroutine of make_table when base region has ARRAY_TYPE. */
1359 void add_array_elements_to_table (table
&t
,
1360 const bit_to_table_map
&btm
,
1361 style_manager
&sm
) const
1363 tree base_type
= m_op
.m_base_region
->get_type ();
1364 gcc_assert (TREE_CODE (base_type
) == ARRAY_TYPE
);
1365 gcc_assert (m_boundaries
!= nullptr);
1367 tree domain
= TYPE_DOMAIN (base_type
);
1368 if (!(domain
&& TYPE_MIN_VALUE (domain
) && TYPE_MAX_VALUE (domain
)))
1371 const int table_y
= 0;
1372 const int table_h
= 1;
1373 const table::range_t
table_y_range (table_y
, table_y
+ table_h
);
1377 const table::range_t min_x_range
1378 = maybe_add_array_index_to_table (t
, btm
, sm
, table_y_range
,
1379 TYPE_MIN_VALUE (domain
));
1380 const table::range_t max_x_range
1381 = maybe_add_array_index_to_table (t
, btm
, sm
, table_y_range
,
1382 TYPE_MAX_VALUE (domain
));
1384 if (TREE_TYPE (base_type
) == char_type_node
)
1386 /* For a char array,: if there are any hard boundaries in
1387 m_boundaries that are *within* the valid region,
1388 then show those index values. */
1389 std::vector
<region_offset
> hard_boundaries
1390 = m_boundaries
->get_hard_boundaries_in_range
1391 (tree_to_shwi (TYPE_MIN_VALUE (domain
)),
1392 tree_to_shwi (TYPE_MAX_VALUE (domain
)));
1393 for (auto &offset
: hard_boundaries
)
1395 const int table_x
= btm
.get_table_x_for_offset (offset
);
1396 if (!offset
.concrete_p ())
1399 if (!offset
.get_concrete_byte_offset (&byte
))
1401 table::range_t
table_x_range (table_x
, table_x
+ 1);
1402 t
.maybe_set_cell_span (table::rect_t (table_x_range
,
1404 fmt_styled_string (sm
, "[%wi]",
1409 add_ellipsis_to_gaps (t
, sm
,
1410 table::range_t (min_x_range
.get_next (),
1411 max_x_range
.get_min ()),
1416 maybe_add_array_index_to_table (table
&t
,
1417 const bit_to_table_map
&btm
,
1419 const table::range_t table_y_range
,
1422 region_model_manager
* const mgr
= m_op
.get_manager ();
1423 tree base_type
= m_op
.m_base_region
->get_type ();
1424 const svalue
*idx_sval
1425 = mgr
->get_or_create_constant_svalue (idx_cst
);
1426 const region
*element_reg
= mgr
->get_element_region (m_op
.m_base_region
,
1427 TREE_TYPE (base_type
),
1429 const access_range
element_range (*element_reg
, mgr
);
1430 const table::range_t element_x_range
1431 = btm
.get_table_x_for_range (element_range
);
1433 t
.maybe_set_cell_span (table::rect_t (element_x_range
,
1435 fmt_styled_string (sm
, "[%E]", idx_cst
));
1437 return element_x_range
;
1440 table
make_table (const bit_to_table_map
&btm
,
1441 style_manager
&sm
) const final override
1443 table
t (table::size_t (btm
.get_num_columns (), 0));
1445 if (tree base_type
= m_op
.m_base_region
->get_type ())
1446 if (TREE_CODE (base_type
) == ARRAY_TYPE
)
1447 add_array_elements_to_table (t
, btm
, sm
);
1449 /* Make use of m_existing_sval_spatial_item, if any. */
1450 if (m_existing_sval_spatial_item
)
1452 table table_for_existing
1453 = m_existing_sval_spatial_item
->make_table (btm
, sm
);
1454 const int table_y
= t
.add_rows (table_for_existing
.get_size ().h
);
1455 t
.add_other_table (std::move (table_for_existing
),
1456 table::coord_t (0, table_y
));
1459 access_range valid_bits
= m_op
.get_valid_bits ();
1460 const int table_y
= t
.add_row ();
1461 const int table_h
= 1;
1462 table::rect_t rect
= btm
.get_table_rect (valid_bits
, table_y
, table_h
);
1464 switch (m_op
.m_base_region
->get_kind ())
1467 s
= styled_string (sm
, _("region"));
1471 const decl_region
*decl_reg
1472 = as_a
<const decl_region
*> (m_op
.m_base_region
);
1473 tree decl
= decl_reg
->get_decl ();
1474 s
= fmt_styled_string (sm
, "%qE (type: %qT)",
1479 case RK_HEAP_ALLOCATED
:
1481 if (m_region_creation_event_id
.known_p ())
1482 s
= fmt_styled_string (sm
, _("buffer allocated on heap at %@"),
1483 &m_region_creation_event_id
);
1485 s
= styled_string (sm
, _("heap-allocated buffer"));
1490 if (m_region_creation_event_id
.known_p ())
1491 s
= fmt_styled_string (sm
, _("buffer allocated on stack at %@"),
1492 &m_region_creation_event_id
);
1494 s
= styled_string (sm
, _("stack-allocated buffer"));
1499 const string_region
*string_reg
1500 = as_a
<const string_region
*> (m_op
.m_base_region
);
1501 tree string_cst
= string_reg
->get_string_cst ();
1502 s
= fmt_styled_string (sm
, _("string literal (type: %qT)"),
1503 TREE_TYPE (string_cst
));
1507 t
.set_cell_span (rect
, std::move (s
));
1513 const access_operation
&m_op
;
1514 diagnostic_event_id_t m_region_creation_event_id
;
1515 mutable const boundaries
*m_boundaries
;
1516 const svalue
*m_existing_sval
;
1517 std::unique_ptr
<spatial_item
> m_existing_sval_spatial_item
;
1520 /* Subclass of spatial_item for visualizing the region of memory
1521 that's actually accessed by the read or write, for reads and
1522 for write cases where we don't know the svalue written. */
1524 class accessed_region_spatial_item
: public spatial_item
1527 accessed_region_spatial_item (const access_operation
&op
) : m_op (op
) {}
1529 void add_boundaries (boundaries
&out
, logger
*logger
) const final override
1532 access_range actual_bits
= m_op
.get_actual_bits ();
1535 logger
->start_log_line ();
1536 logger
->log_partial ("actual bits: ");
1537 actual_bits
.dump_to_pp (logger
->get_printer (), true);
1538 logger
->end_log_line ();
1540 out
.add (actual_bits
, boundaries::kind::HARD
);
1543 table
make_table (const bit_to_table_map
&btm
,
1544 style_manager
&sm
) const final override
1546 table
t (table::size_t (btm
.get_num_columns (), 1));
1548 access_range actual_bits
= m_op
.get_actual_bits ();
1549 const int table_y
= 0;
1550 const int table_h
= 1;
1551 table::rect_t rect
= btm
.get_table_rect (actual_bits
, table_y
, table_h
);
1552 t
.set_cell_span (rect
, styled_string (get_label_string (sm
)));
1558 styled_string
get_label_string (style_manager
&sm
) const
1560 const access_range
accessed_bits (m_op
.get_actual_bits ());
1561 return get_access_size_str (sm
,
1564 m_op
.m_reg
.get_type ());
1567 const access_operation
&m_op
;
1570 /* Subclass of spatial_item for when we know the svalue being written
1571 to the accessed region.
1572 Can be subclassed to give visualizations of specific kinds of svalue. */
1574 class written_svalue_spatial_item
: public spatial_item
1577 written_svalue_spatial_item (const access_operation
&op
,
1579 access_range actual_bits
)
1580 : m_op (op
), m_sval (sval
), m_actual_bits (actual_bits
)
1583 void add_boundaries (boundaries
&out
, logger
*logger
) const override
1586 out
.add (m_actual_bits
, boundaries::kind::HARD
);
1589 table
make_table (const bit_to_table_map
&btm
,
1590 style_manager
&sm
) const override
1592 table
t (table::size_t (btm
.get_num_columns (), 0));
1594 const int table_y
= t
.add_row ();
1595 const int table_h
= 1;
1596 table::rect_t rect
= btm
.get_table_rect (m_actual_bits
, table_y
, table_h
);
1597 t
.set_cell_span (rect
, styled_string (get_label_string (sm
)));
1602 styled_string
get_label_string (style_manager
&sm
) const
1604 tree rep_tree
= m_op
.m_model
.get_representative_tree (&m_sval
);
1607 if (TREE_CODE (rep_tree
) == SSA_NAME
)
1608 rep_tree
= SSA_NAME_VAR (rep_tree
);
1609 switch (TREE_CODE (rep_tree
))
1614 return fmt_styled_string (sm
, _("write of %<(%T) %E%>"),
1615 TREE_TYPE (rep_tree
),
1620 return fmt_styled_string (sm
, _("write from %qE (type: %qT)"),
1622 TREE_TYPE (rep_tree
));
1627 const access_range
accessed_bits (m_op
.get_actual_bits ());
1628 return get_access_size_str (sm
,
1631 m_sval
.get_type ());
1634 const access_operation
&m_op
;
1635 const svalue
&m_sval
;
1636 access_range m_actual_bits
;
1639 /* Subclass of svalue_spatial_item for initial_svalue of a string_region
1640 i.e. for string literals.
1642 There are three cases:
1643 (a) for long strings, show just the head and tail of the string,
1645 +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
1646 |[0]|[1]|[2]|[3]|[4]|[5]| |[440]|[441]|[442]|[443]|[444]|[445]|
1647 +---+---+---+---+---+---+ ... +-----+-----+-----+-----+-----+-----+
1648 |‘L’|‘o’|‘r’|‘e’|‘m’|‘ ’| | ‘o’ | ‘r’ | ‘u’ | ‘m’ | ‘.’ | NUL |
1649 +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
1650 | string literal (type: ‘char[446]’) |
1651 +----------------------------------------------------------------------+
1652 (b) For sufficiently short strings, show the full string:
1653 +----------+---------+---------+---------+---------+ +-----------------+
1654 | [0] | [1] | [2] | [3] | [4] | | [5] |
1655 +----------+---------+---------+---------+---------+ +-----------------+
1656 | ‘h’ | ‘e’ | ‘l’ | ‘l’ | ‘o’ | | NUL |
1657 +----------+---------+---------+---------+---------+-+-----------------+
1658 | string literal (type: ‘char[6]’) |
1659 +----------------------------------------------------------------------+
1660 (c) for non-ASCII strings that are short enough to show the full string,
1661 show how unicode code points of the bytes decoded as UTF-8:
1662 +-----+-----+-----+----+----++----+----+----+----+----+----+----+------+
1663 | [0] | [1] | [2] |[3] |[4] ||[5] |[6] |[7] |[8] |[9] |[10]|[11]| [12] |
1664 +-----+-----+-----+----+----++----+----+----+----+----+----+----+------+
1665 |0xe6 |0x96 |0x87 |0xe5|0xad||0x97|0xe5|0x8c|0x96|0xe3|0x81|0x91| 0x00 |
1666 +-----+-----+-----+----+----++----+----+----+----+----+----+----+------+
1667 | U+6587 | U+5b57 | U+5316 | U+3051 |U+0000|
1668 +-----------------+---------------+--------------+--------------+------+
1669 | string literal (type: ‘char[13]’) |
1670 +----------------------------------------------------------------------+
1671 and show the characters themselves if unicode is supported and they are not
1673 ┌─────┬─────┬─────┬────┬────┐┌────┬────┬────┬────┬────┬────┬────┬──────┐
1674 │ [0] │ [1] │ [2] │[3] │[4] ││[5] │[6] │[7] │[8] │[9] │[10]│[11]│ [12] │
1675 ├─────┼─────┼─────┼────┼────┤├────┼────┼────┼────┼────┼────┼────┼──────┤
1676 │0xe6 │0x96 │0x87 │0xe5│0xad││0x97│0xe5│0x8c│0x96│0xe3│0x81│0x91│ 0x00 │
1677 ├─────┴─────┴─────┼────┴────┴┴────┼────┴────┴────┼────┴────┴────┼──────┤
1678 │ U+6587 │ U+5b57 │ U+5316 │ U+3051 │U+0000│
1679 ├─────────────────┼───────────────┼──────────────┼──────────────┼──────┤
1680 │ 文 │ 字 │ 化 │ け │ NUL │
1681 ├─────────────────┴───────────────┴──────────────┴──────────────┴──────┤
1682 │ string literal (type: ‘char[13]’) │
1683 └──────────────────────────────────────────────────────────────────────┘
1686 class string_literal_spatial_item
: public svalue_spatial_item
1689 string_literal_spatial_item (const svalue
&sval
,
1690 access_range actual_bits
,
1691 const string_region
&string_reg
,
1694 : svalue_spatial_item (sval
, actual_bits
, kind
),
1695 m_string_reg (string_reg
),
1697 m_ellipsis_threshold (param_analyzer_text_art_string_ellipsis_threshold
),
1698 m_ellipsis_head_len (param_analyzer_text_art_string_ellipsis_head_len
),
1699 m_ellipsis_tail_len (param_analyzer_text_art_string_ellipsis_tail_len
),
1700 m_show_full_string (calc_show_full_string ()),
1701 m_show_utf8 (m_show_full_string
&& !pure_ascii_p ())
1705 void add_boundaries (boundaries
&out
, logger
*logger
) const override
1708 out
.add (m_bits
, m_kind
== svalue_spatial_item::kind::WRITTEN
1709 ? boundaries::kind::HARD
1710 : boundaries::kind::SOFT
);
1712 tree string_cst
= get_string_cst ();
1713 /* TREE_STRING_LENGTH is sizeof, not strlen. */
1714 if (m_show_full_string
)
1715 out
.add_all_bytes_in_range (m_bits
);
1718 byte_range
bytes (0, 0);
1719 bool valid
= m_bits
.as_concrete_byte_range (&bytes
);
1721 byte_range
head_of_string (bytes
.get_start_byte_offset (),
1722 m_ellipsis_head_len
);
1723 out
.add_all_bytes_in_range (head_of_string
);
1724 byte_range tail_of_string
1725 ((bytes
.get_start_byte_offset ()
1726 + TREE_STRING_LENGTH (string_cst
)
1727 - m_ellipsis_tail_len
),
1728 m_ellipsis_tail_len
);
1729 out
.add_all_bytes_in_range (tail_of_string
);
1730 /* Adding the above pair of ranges will also effectively add
1731 the boundaries of the range of ellipsized chars, as they're
1732 exactly in between head_of_string and tail_of_string. */
1736 table
make_table (const bit_to_table_map
&btm
,
1737 style_manager
&sm
) const override
1739 table
t (table::size_t (btm
.get_num_columns (), 0));
1741 const int byte_idx_table_y
= (m_kind
== svalue_spatial_item::kind::WRITTEN
1744 const int byte_val_table_y
= t
.add_row ();
1746 byte_range
bytes (0, 0);
1747 bool valid
= m_bits
.as_concrete_byte_range (&bytes
);
1749 tree string_cst
= get_string_cst ();
1750 if (m_show_full_string
)
1752 for (byte_offset_t byte_idx_within_cluster
1753 = bytes
.get_start_byte_offset ();
1754 byte_idx_within_cluster
< bytes
.get_next_byte_offset ();
1755 byte_idx_within_cluster
= byte_idx_within_cluster
+ 1)
1757 (t
, btm
, sm
, byte_idx_within_cluster
,
1758 byte_idx_within_cluster
- bytes
.get_start_byte_offset (),
1759 byte_idx_table_y
, byte_val_table_y
);
1763 const bool show_unichars
= m_theme
.unicode_p ();
1764 const int utf8_code_point_table_y
= t
.add_row ();
1765 int utf8_character_table_y
;
1767 utf8_character_table_y
= t
.add_row ();
1769 /* We don't actually want the display widths here, but
1770 it's an easy way to decode UTF-8. */
1771 cpp_char_column_policy
policy (8, cpp_wcwidth
);
1772 cpp_display_width_computation
dw (TREE_STRING_POINTER (string_cst
),
1773 TREE_STRING_LENGTH (string_cst
),
1777 cpp_decoded_char decoded_char
;
1778 dw
.process_next_codepoint (&decoded_char
);
1780 if (!decoded_char
.m_valid_ch
)
1782 size_t start_byte_idx
1783 = decoded_char
.m_start_byte
- TREE_STRING_POINTER (string_cst
);
1784 byte_size_t size_in_bytes
1785 = decoded_char
.m_next_byte
- decoded_char
.m_start_byte
;
1786 byte_range cluster_bytes_for_codepoint
1787 (start_byte_idx
+ bytes
.get_start_byte_offset (),
1790 const table::rect_t code_point_table_rect
1791 = btm
.get_table_rect (&m_string_reg
,
1792 cluster_bytes_for_codepoint
,
1793 utf8_code_point_table_y
, 1);
1795 sprintf (buf
, "U+%04x", decoded_char
.m_ch
);
1796 t
.set_cell_span (code_point_table_rect
,
1797 styled_string (sm
, buf
));
1801 const table::rect_t character_table_rect
1802 = btm
.get_table_rect (&m_string_reg
,
1803 cluster_bytes_for_codepoint
,
1804 utf8_character_table_y
, 1);
1805 if (cpp_is_printable_char (decoded_char
.m_ch
))
1806 t
.set_cell_span (character_table_rect
,
1807 styled_string (decoded_char
.m_ch
));
1808 else if (decoded_char
.m_ch
== 0)
1809 t
.set_cell_span (character_table_rect
,
1810 styled_string (sm
, "NUL"));
1812 t
.set_cell_span (character_table_rect
,
1813 styled_string (sm
, ""));
1820 /* Head of string. */
1821 for (int byte_idx
= 0; byte_idx
< m_ellipsis_head_len
; byte_idx
++)
1822 add_column_for_byte (t
, btm
, sm
,
1823 byte_idx
+ bytes
.get_start_byte_offset (),
1825 byte_idx_table_y
, byte_val_table_y
);
1828 const byte_range ellipsis_bytes
1829 (m_ellipsis_head_len
+ bytes
.get_start_byte_offset (),
1830 TREE_STRING_LENGTH (string_cst
)
1831 - (m_ellipsis_head_len
+ m_ellipsis_tail_len
));
1832 const table::rect_t table_rect
1833 = ((byte_idx_table_y
!= -1)
1834 ? btm
.get_table_rect (&m_string_reg
, ellipsis_bytes
,
1835 byte_idx_table_y
, 2)
1836 : btm
.get_table_rect (&m_string_reg
, ellipsis_bytes
,
1837 byte_val_table_y
, 1));
1838 t
.set_cell_span(table_rect
, styled_string (sm
, "..."));
1840 /* Tail of string. */
1842 = (TREE_STRING_LENGTH (string_cst
) - m_ellipsis_tail_len
);
1843 byte_idx
< TREE_STRING_LENGTH (string_cst
);
1845 add_column_for_byte (t
, btm
, sm
,
1846 byte_idx
+ bytes
.get_start_byte_offset (),
1848 byte_idx_table_y
, byte_val_table_y
);
1851 if (m_kind
== svalue_spatial_item::kind::WRITTEN
)
1853 const int summary_table_y
= t
.add_row ();
1854 t
.set_cell_span (btm
.get_table_rect (&m_string_reg
, bytes
,
1855 summary_table_y
, 1),
1856 fmt_styled_string (sm
,
1857 _("string literal (type: %qT)"),
1858 TREE_TYPE (string_cst
)));
1864 tree
get_string_cst () const { return m_string_reg
.get_string_cst (); }
1867 bool calc_show_full_string () const
1869 tree string_cst
= get_string_cst ();
1870 if (TREE_STRING_LENGTH (string_cst
) < m_ellipsis_threshold
)
1872 if (TREE_STRING_LENGTH (string_cst
) <
1873 (m_ellipsis_head_len
+ m_ellipsis_tail_len
))
1878 bool pure_ascii_p () const
1880 tree string_cst
= get_string_cst ();
1881 for (unsigned byte_idx
= 0;
1882 byte_idx
< (unsigned) TREE_STRING_LENGTH (string_cst
);
1885 unsigned char ch
= TREE_STRING_POINTER (string_cst
)[byte_idx
];
1892 void add_column_for_byte (table
&t
, const bit_to_table_map
&btm
,
1894 const byte_offset_t byte_idx_within_cluster
,
1895 const byte_offset_t byte_idx_within_string
,
1896 const int byte_idx_table_y
,
1897 const int byte_val_table_y
) const
1899 tree string_cst
= get_string_cst ();
1900 gcc_assert (byte_idx_within_string
>= 0);
1901 gcc_assert (byte_idx_within_string
< TREE_STRING_LENGTH (string_cst
));
1903 const byte_range
bytes (byte_idx_within_cluster
, 1);
1904 if (byte_idx_table_y
!= -1)
1906 const table::rect_t idx_table_rect
1907 = btm
.get_table_rect (&m_string_reg
, bytes
, byte_idx_table_y
, 1);
1908 t
.set_cell_span (idx_table_rect
,
1909 fmt_styled_string (sm
, "[%wu]",
1910 byte_idx_within_string
.ulow ()));
1914 = TREE_STRING_POINTER (string_cst
)[byte_idx_within_string
.ulow ()];
1915 const table::rect_t val_table_rect
1916 = btm
.get_table_rect (&m_string_reg
, bytes
, byte_val_table_y
, 1);
1917 table_cell_content
content (make_cell_content_for_byte (sm
, byte_val
));
1918 t
.set_cell_span (val_table_rect
, std::move (content
));
1921 table_cell_content
make_cell_content_for_byte (style_manager
&sm
,
1922 unsigned char byte_val
) const
1926 if (byte_val
== '\0')
1927 return styled_string (sm
, "NUL");
1928 else if (byte_val
< 0x80)
1929 if (ISPRINT (byte_val
))
1930 return fmt_styled_string (sm
, "%qc", byte_val
);
1933 sprintf (buf
, "0x%02x", byte_val
);
1934 return styled_string (sm
, buf
);
1937 const string_region
&m_string_reg
;
1938 const theme
&m_theme
;
1939 const int m_ellipsis_threshold
;
1940 const int m_ellipsis_head_len
;
1941 const int m_ellipsis_tail_len
;
1942 const bool m_show_full_string
;
1943 const bool m_show_utf8
;
1946 static std::unique_ptr
<spatial_item
>
1947 make_written_svalue_spatial_item (const access_operation
&op
,
1949 access_range actual_bits
,
1952 if (const initial_svalue
*initial_sval
= sval
.dyn_cast_initial_svalue ())
1953 if (const string_region
*string_reg
1954 = initial_sval
->get_region ()->dyn_cast_string_region ())
1955 return make_unique
<string_literal_spatial_item
>
1958 svalue_spatial_item::kind::WRITTEN
);
1959 return make_unique
<written_svalue_spatial_item
> (op
, sval
, actual_bits
);
1962 static std::unique_ptr
<spatial_item
>
1963 make_existing_svalue_spatial_item (const svalue
*sval
,
1964 const access_range
&bits
,
1970 switch (sval
->get_kind ())
1977 const initial_svalue
*initial_sval
= (const initial_svalue
*)sval
;
1978 if (const string_region
*string_reg
1979 = initial_sval
->get_region ()->dyn_cast_string_region ())
1980 return make_unique
<string_literal_spatial_item
>
1983 svalue_spatial_item::kind::EXISTING
);
1988 return make_unique
<compound_svalue_spatial_item
>
1989 (*((const compound_svalue
*)sval
),
1991 svalue_spatial_item::kind::EXISTING
,
1996 /* Widget subclass implementing access diagrams. */
1998 class access_diagram_impl
: public vbox_widget
2001 access_diagram_impl (const access_operation
&op
,
2002 diagnostic_event_id_t region_creation_event_id
,
2007 m_region_creation_event_id (region_creation_event_id
),
2012 m_valid_region_spatial_item (op
, region_creation_event_id
, theme
),
2013 m_accessed_region_spatial_item (op
),
2015 m_calc_req_size_called (false)
2021 access_range invalid_before_bits
;
2022 if (op
.maybe_get_invalid_before_bits (&invalid_before_bits
))
2023 invalid_before_bits
.log ("invalid before range", *logger
);
2024 access_range invalid_after_bits
;
2025 if (op
.maybe_get_invalid_after_bits (&invalid_after_bits
))
2026 invalid_after_bits
.log ("invalid after range", *logger
);
2030 logger
->start_log_line ();
2031 logger
->log_partial ("sval_hint: ");
2032 op
.m_sval_hint
->dump_to_pp (logger
->get_printer (), true);
2033 logger
->end_log_line ();
2037 /* Register painting styles. */
2040 valid_style
.m_fg_color
= style::named_color::GREEN
;
2041 valid_style
.m_bold
= true;
2042 m_valid_style_id
= m_sm
.get_or_create_id (valid_style
);
2044 style invalid_style
;
2045 invalid_style
.m_fg_color
= style::named_color::RED
;
2046 invalid_style
.m_bold
= true;
2047 m_invalid_style_id
= m_sm
.get_or_create_id (invalid_style
);
2052 access_range actual_bits
= m_op
.get_actual_bits ();
2053 m_written_svalue_spatial_item
2054 = make_written_svalue_spatial_item (m_op
,
2061 First, figure out all of the boundaries of interest.
2062 Then use that to build child widgets showing the regions of interest,
2063 with a common tabular layout. */
2065 m_boundaries
= find_boundaries ();
2067 m_boundaries
->log (*logger
);
2069 /* Populate m_table_x_for_bit and m_bit_for_table_x.
2070 Each table column represents the range [offset, next_offset).
2071 We don't create a column in the table for the final offset, but we
2072 do populate it, so that looking at the table_x of one beyond the
2073 final table column gives us the upper bound offset. */
2074 m_btm
.populate (*m_boundaries
, logger
);
2076 /* Gracefully reject cases where the boundary sorting has gone wrong
2077 (due to awkward combinations of symbolic values). */
2079 table::range_t actual_bits_x_range
2080 = m_btm
.get_table_x_for_range (m_op
.get_actual_bits ());
2081 if (actual_bits_x_range
.get_size () <= 0)
2084 logger
->log ("giving up: bad table columns for actual_bits");
2088 table::range_t valid_bits_x_range
2089 = m_btm
.get_table_x_for_range (m_op
.get_valid_bits ());
2090 if (valid_bits_x_range
.get_size () <= 0)
2093 logger
->log ("giving up: bad table columns for valid_bits");
2100 = make_unique
<table_dimension_sizes
> (m_btm
.get_num_columns ());
2102 /* Now create child widgets. */
2104 if (flag_analyzer_debug_text_art
)
2106 table
t_headings (make_headings_table ());
2107 add_aligned_child_table (std::move (t_headings
));
2110 if (m_written_svalue_spatial_item
)
2112 table
t_sval (m_written_svalue_spatial_item
->make_table (m_btm
, m_sm
));
2113 add_aligned_child_table (std::move (t_sval
));
2118 (m_accessed_region_spatial_item
.make_table (m_btm
, m_sm
));
2119 add_aligned_child_table (std::move (t_accessed
));
2122 add_direction_widget ();
2124 table
t_valid (m_valid_region_spatial_item
.make_table (m_btm
, m_sm
));
2125 add_invalid_accesses_to_region_table (t_valid
);
2126 add_aligned_child_table (std::move (t_valid
));
2128 add_valid_vs_invalid_ruler ();
2131 const char *get_desc () const override
2133 return "access_diagram_impl";
2136 canvas::size_t calc_req_size () final override
2139 return canvas::size_t (0, 0);
2141 /* Now compute the size requirements for the tables. */
2142 for (auto iter
: m_aligned_table_widgets
)
2143 iter
->get_cell_sizes ().pass_1 (iter
->get_table ());
2144 for (auto iter
: m_aligned_table_widgets
)
2145 iter
->get_cell_sizes ().pass_2 (iter
->get_table ());
2149 /* ...and relayout the tables. */
2150 for (auto iter
: m_aligned_table_widgets
)
2151 iter
->recalc_coords ();
2153 /* Populate the canvas_x per table_x. */
2154 m_col_start_x
.clear ();
2155 int iter_canvas_x
= 0;
2156 for (auto w
: m_col_widths
->m_requirements
)
2158 m_col_start_x
.push_back (iter_canvas_x
);
2159 iter_canvas_x
+= w
+ 1;
2161 m_col_start_x
.push_back (iter_canvas_x
);
2163 m_calc_req_size_called
= true;
2165 return vbox_widget::calc_req_size ();
2168 int get_canvas_x_for_table_x (int table_x
) const
2170 gcc_assert (m_calc_req_size_called
);
2171 return m_col_start_x
[table_x
];
2174 canvas::range_t
get_canvas_x_range (const table::range_t
&table_x_range
) const
2176 gcc_assert (m_calc_req_size_called
);
2177 return canvas::range_t (get_canvas_x_for_table_x (table_x_range
.start
),
2178 get_canvas_x_for_table_x (table_x_range
.next
));
2181 const access_operation
&get_op () const { return m_op
; }
2183 style::id_t
get_style_id_for_validity (bool is_valid
) const
2185 return is_valid
? m_valid_style_id
: m_invalid_style_id
;
2188 const theme
&get_theme () const { return m_theme
; }
2191 /* Figure out all of the boundaries of interest when visualizing ths op. */
2192 std::unique_ptr
<boundaries
>
2193 find_boundaries () const
2195 std::unique_ptr
<boundaries
> result
2196 = make_unique
<boundaries
> (*m_op
.m_base_region
, m_logger
);
2198 m_valid_region_spatial_item
.add_boundaries (*result
, m_logger
);
2199 m_accessed_region_spatial_item
.add_boundaries (*result
, m_logger
);
2200 if (m_written_svalue_spatial_item
)
2201 m_written_svalue_spatial_item
->add_boundaries (*result
, m_logger
);
2206 void add_aligned_child_table (table t
)
2208 x_aligned_table_widget
*w
2209 = new x_aligned_table_widget (std::move (t
), m_theme
, *m_col_widths
);
2210 m_aligned_table_widgets
.push_back (w
);
2211 add_child (std::unique_ptr
<widget
> (w
));
2214 /* Create a table showing headings for use by -fanalyzer-debug-text-art, for
2216 +---------+-----------+-----------+---+--------------------------------+
2217 | tc0 | tc1 | tc2 |tc3| tc4 |
2218 +---------+-----------+-----------+---+--------------------------------+
2219 |bytes 0-3|bytes 4-35 |bytes 36-39| | bytes 40-43 |
2220 +---------+-----------+-----------+ +--------------------------------+
2222 - a row showing the table column numbers, labelled "tc0", "tc1", etc
2223 - a row showing the memory range of each table column that has one. */
2225 table
make_headings_table () const
2227 table
t (table::size_t (m_btm
.get_num_columns (), 2));
2229 for (int table_x
= 0; table_x
< t
.get_size ().w
; table_x
++)
2231 const int table_y
= 0;
2232 t
.set_cell (table::coord_t (table_x
, table_y
),
2233 fmt_styled_string (m_sm
, "tc%i", table_x
));
2235 for (int table_x
= 0; table_x
< t
.get_size ().w
; table_x
++)
2237 const int table_y
= 1;
2238 access_range
range_for_column (NULL
, bit_range (0, 0));
2239 if (m_btm
.maybe_get_access_range_for_table_x (table_x
,
2243 pp_format_decoder (&pp
) = default_tree_printer
;
2244 range_for_column
.dump_to_pp (&pp
, true);
2245 t
.set_cell (table::coord_t (table_x
, table_y
),
2246 styled_string (m_sm
, pp_formatted_text (&pp
)));
2253 void add_direction_widget ()
2255 add_child (::make_unique
<direction_widget
> (*this, m_btm
));
2258 void add_invalid_accesses_to_region_table (table
&t_region
)
2260 gcc_assert (t_region
.get_size ().w
== (int)m_btm
.get_num_columns ());
2262 const int table_y
= 0;
2263 const int table_h
= t_region
.get_size ().h
;
2265 access_range invalid_before_bits
;
2266 if (m_op
.maybe_get_invalid_before_bits (&invalid_before_bits
))
2268 t_region
.set_cell_span (m_btm
.get_table_rect (invalid_before_bits
,
2270 styled_string (m_sm
,
2271 _("before valid range")));
2273 access_range invalid_after_bits
;
2274 if (m_op
.maybe_get_invalid_after_bits (&invalid_after_bits
))
2276 t_region
.set_cell_span (m_btm
.get_table_rect (invalid_after_bits
,
2278 styled_string (m_sm
,
2279 _("after valid range")));
2283 void maybe_add_gap (x_aligned_x_ruler_widget
*w
,
2284 const access_range
&lower
,
2285 const access_range
&upper
) const
2287 LOG_SCOPE (m_logger
);
2290 lower
.log ("lower", *m_logger
);
2291 upper
.log ("upper", *m_logger
);
2293 tree lower_next
= lower
.m_next
.calc_symbolic_bit_offset (m_op
.m_model
);
2297 m_logger
->log ("failed to get lower_next");
2300 tree upper_start
= upper
.m_start
.calc_symbolic_bit_offset (m_op
.m_model
);
2304 m_logger
->log ("failed to get upper_start");
2307 tree num_bits_gap
= fold_build2 (MINUS_EXPR
,
2309 upper_start
, lower_next
);
2311 m_logger
->log ("num_bits_gap: %qE", num_bits_gap
);
2312 tree zero
= build_int_cst (size_type_node
, 0);
2313 tristate ts_gt_zero
= m_op
.m_model
.eval_condition (num_bits_gap
,
2317 if (ts_gt_zero
.is_false ())
2320 m_logger
->log ("rejecting as not > 0");
2324 bit_size_expr
num_bits (num_bits_gap
);
2325 styled_string label
= num_bits
.get_formatted_str (m_sm
,
2332 w
->add_range (m_btm
.get_table_x_for_range (access_range (lower
.m_next
,
2339 make_warning_string (styled_string
&&text
)
2341 styled_string result
;
2342 if (!m_theme
.emojis_p ())
2343 return std::move (text
);
2345 result
.append (styled_string (0x26A0, /* U+26A0 WARNING SIGN. */
2347 /* U+26A0 WARNING SIGN has East_Asian_Width == Neutral, but in its
2348 emoji variant is printed (by vte at least) with a 2nd half
2349 overlapping the next char. Hence we add two spaces here: a space
2350 to be covered by this overlap, plus another space of padding. */
2351 result
.append (styled_string (m_sm
, " "));
2352 result
.append (std::move (text
));
2356 /* Add a ruler child widet showing valid, invalid, and gaps. */
2357 void add_valid_vs_invalid_ruler ()
2359 LOG_SCOPE (m_logger
);
2361 x_aligned_x_ruler_widget
*w
2362 = new x_aligned_x_ruler_widget (*this, m_theme
);
2364 access_range invalid_before_bits
;
2365 if (m_op
.maybe_get_invalid_before_bits (&invalid_before_bits
))
2368 invalid_before_bits
.log ("invalid_before_bits", *m_logger
);
2369 bit_size_expr num_before_bits
;
2370 if (invalid_before_bits
.get_size (m_op
.m_model
, &num_before_bits
))
2372 styled_string label
;
2373 if (m_op
.m_dir
== DIR_READ
)
2374 label
= num_before_bits
.get_formatted_str
2376 _("under-read of %wi bit"),
2377 _("under-read of %wi bits"),
2378 _("under-read of %wi byte"),
2379 _("under-read of %wi bytes"),
2380 _("under-read of %qE bits"),
2381 _("under-read of %qE bytes"));
2383 label
= num_before_bits
.get_formatted_str
2385 _("underwrite of %wi bit"),
2386 _("underwrite of %wi bits"),
2387 _("underwrite of %wi byte"),
2388 _("underwrite of %wi bytes"),
2389 _("underwrite of %qE bits"),
2390 _("underwrite of %qE bytes"));
2391 w
->add_range (m_btm
.get_table_x_for_range (invalid_before_bits
),
2392 make_warning_string (std::move (label
)),
2393 m_invalid_style_id
);
2399 m_logger
->log ("no invalid_before_bits");
2402 /* It would be nice to be able to use std::optional<access_range> here,
2403 but std::optional is C++17. */
2404 bool got_valid_bits
= false;
2405 access_range
valid_bits (m_op
.get_valid_bits ());
2406 bit_size_expr num_valid_bits
;
2407 if (valid_bits
.get_size (m_op
.m_model
, &num_valid_bits
))
2410 valid_bits
.log ("valid_bits", *m_logger
);
2412 got_valid_bits
= true;
2413 maybe_add_gap (w
, invalid_before_bits
, valid_bits
);
2415 styled_string label
;
2416 if (m_op
.m_dir
== DIR_READ
)
2417 label
= num_valid_bits
.get_formatted_str (m_sm
,
2419 _("size: %wi bits"),
2420 _("size: %wi byte"),
2421 _("size: %wi bytes"),
2422 _("size: %qE bits"),
2423 _("size: %qE bytes"));
2425 label
= num_valid_bits
.get_formatted_str (m_sm
,
2426 _("capacity: %wi bit"),
2427 _("capacity: %wi bits"),
2428 _("capacity: %wi byte"),
2429 _("capacity: %wi bytes"),
2430 _("capacity: %qE bits"),
2431 _("capacity: %qE bytes"));
2432 w
->add_range (m_btm
.get_table_x_for_range (m_op
.get_valid_bits ()),
2437 access_range invalid_after_bits
;
2438 if (m_op
.maybe_get_invalid_after_bits (&invalid_after_bits
))
2441 maybe_add_gap (w
, valid_bits
, invalid_after_bits
);
2444 invalid_before_bits
.log ("invalid_after_bits", *m_logger
);
2446 bit_size_expr num_after_bits
;
2447 if (invalid_after_bits
.get_size (m_op
.m_model
, &num_after_bits
))
2449 styled_string label
;
2450 if (m_op
.m_dir
== DIR_READ
)
2451 label
= num_after_bits
.get_formatted_str
2453 _("over-read of %wi bit"),
2454 _("over-read of %wi bits"),
2455 _("over-read of %wi byte"),
2456 _("over-read of %wi bytes"),
2457 _("over-read of %qE bits"),
2458 _("over-read of %qE bytes"));
2460 label
= num_after_bits
.get_formatted_str
2462 _("overflow of %wi bit"),
2463 _("overflow of %wi bits"),
2464 _("overflow of %wi byte"),
2465 _("overflow of %wi bytes"),
2466 _("over-read of %qE bits"),
2467 _("overflow of %qE bytes"));
2468 w
->add_range (m_btm
.get_table_x_for_range (invalid_after_bits
),
2469 make_warning_string (std::move (label
)),
2470 m_invalid_style_id
);
2476 m_logger
->log ("no invalid_after_bits");
2479 add_child (std::unique_ptr
<widget
> (w
));
2482 /* Subroutine of calc_req_size.
2483 Try to allocate surplus canvas width to table columns to make the
2484 per table-column canvas widths closer to being to scale.
2486 https://en.wikipedia.org/wiki/Fair_item_allocation
2487 https://en.wikipedia.org/wiki/Mathematics_of_apportionment
2489 void adjust_to_scale ()
2491 LOG_SCOPE (m_logger
);
2492 const unsigned num_columns
= m_btm
.get_num_columns ();
2493 std::vector
<bit_offset_t
> bit_sizes (num_columns
);
2494 for (unsigned table_x
= 0; table_x
< num_columns
; table_x
++)
2496 access_range
range_for_column (NULL
, bit_range (0, 0));
2497 if (m_btm
.maybe_get_access_range_for_table_x (table_x
,
2500 bit_size_t size_in_bits
;
2501 if (!range_for_column
.get_size_in_bits (&size_in_bits
))
2502 size_in_bits
= BITS_PER_UNIT
; // arbitrary non-zero value
2503 gcc_assert (size_in_bits
> 0);
2504 bit_sizes
[table_x
] = size_in_bits
;
2507 bit_sizes
[table_x
] = 0;
2510 while (adjust_to_scale_once (bit_sizes
))
2514 bool adjust_to_scale_once (const std::vector
<bit_offset_t
> &bit_sizes
)
2516 LOG_SCOPE (m_logger
);
2518 const unsigned num_columns
= m_btm
.get_num_columns ();
2520 /* Find the total canvas width currently required.
2521 Require one extra canvas column for the right-hand border
2523 int total_width
= 1;
2524 for (unsigned table_x
= 0; table_x
< num_columns
; table_x
++)
2526 int canvas_w
= m_col_widths
->m_requirements
[table_x
];
2527 gcc_assert (canvas_w
>= 0);
2528 total_width
+= canvas_w
+ 1;
2531 const int max_width
= param_analyzer_text_art_ideal_canvas_width
;
2532 if (total_width
>= max_width
)
2535 m_logger
->log ("bailing out: total_width=%i ,>= max_width (%i)\n",
2536 total_width
, max_width
);
2540 const int fixed_point
= 1024;
2541 std::vector
<bit_offset_t
> canvas_w_per_bit (num_columns
);
2542 for (unsigned table_x
= 0; table_x
< num_columns
; table_x
++)
2544 bit_offset_t bit_size
= bit_sizes
[table_x
];
2546 canvas_w_per_bit
[table_x
]
2547 = (m_col_widths
->m_requirements
[table_x
] * fixed_point
) / bit_size
;
2549 canvas_w_per_bit
[table_x
] = INT_MAX
;
2552 /* Find the min canvas per bit, and give an extra canvas column to
2553 the table column that has least. */
2554 size_t min_idx
= std::distance (canvas_w_per_bit
.begin (),
2555 std::min_element (canvas_w_per_bit
.begin (),
2556 canvas_w_per_bit
.end ()));
2557 m_col_widths
->m_requirements
[min_idx
] += 1;
2559 m_logger
->log ("adding 1 canvas_w to column %i\n", (int)min_idx
);
2561 return true; // keep going
2564 const access_operation
&m_op
;
2565 diagnostic_event_id_t m_region_creation_event_id
;
2566 style_manager
&m_sm
;
2567 const theme
&m_theme
;
2569 /* In lieu of being able to throw exceptions, a flag to mark this object
2573 style::id_t m_valid_style_id
;
2574 style::id_t m_invalid_style_id
;
2576 valid_region_spatial_item m_valid_region_spatial_item
;
2577 accessed_region_spatial_item m_accessed_region_spatial_item
;
2578 std::unique_ptr
<spatial_item
> m_written_svalue_spatial_item
;
2580 std::unique_ptr
<boundaries
> m_boundaries
;
2582 bit_to_table_map m_btm
;
2584 bool m_calc_req_size_called
;
2586 /* Column widths shared by all x_aligned_table_widget,
2587 created once we know how many columns we need. */
2588 std::unique_ptr
<table_dimension_sizes
> m_col_widths
;
2590 /* All of the child x_aligned_table_widget that share
2592 std::vector
<x_aligned_table_widget
*> m_aligned_table_widgets
;
2594 /* Mapping from table_x to canvas_x. */
2595 std::vector
<int> m_col_start_x
;
2599 x_aligned_x_ruler_widget::make_x_ruler () const
2601 x_ruler
r (x_ruler::label_dir::BELOW
);
2602 for (auto& iter
: m_labels
)
2604 canvas::range_t canvas_x_range
2605 = m_dia_impl
.get_canvas_x_range (iter
.m_table_x_range
);
2606 /* Include the end-point. */
2607 canvas_x_range
.next
++;
2608 r
.add_label (canvas_x_range
, iter
.m_text
.copy (), iter
.m_style_id
,
2609 x_ruler::label_kind::TEXT_WITH_BORDER
);
2614 /* class direction_widget : public leaf_widget. */
2616 /* Paint arrows indicating the direction of the access (read vs write),
2617 but only in the X-extent corresponding to the region that's actually
2621 direction_widget::paint_to_canvas (canvas
&canvas
)
2623 const access_range
accessed_bits (m_dia_impl
.get_op ().get_actual_bits ());
2625 const access_range
valid_bits (m_dia_impl
.get_op ().get_valid_bits ());
2627 for (unsigned table_x
= 0; table_x
< m_btm
.get_num_columns (); table_x
++)
2629 access_range column_access_range
;
2630 if (m_btm
.maybe_get_access_range_for_table_x (table_x
,
2631 &column_access_range
))
2633 /* Only paint arrows in the accessed region. */
2634 if (!accessed_bits
.contains_p (column_access_range
))
2637 /* Are we within the valid region? */
2638 const bool is_valid (valid_bits
.contains_p (column_access_range
));
2639 const style::id_t style_id
2640 = m_dia_impl
.get_style_id_for_validity (is_valid
);
2641 const canvas::range_t x_canvas_range
2642 = m_dia_impl
.get_canvas_x_range (table::range_t (table_x
,
2644 const int canvas_x
= x_canvas_range
.get_midpoint ();
2645 m_dia_impl
.get_theme ().paint_y_arrow
2648 canvas::range_t (get_y_range ()),
2649 (m_dia_impl
.get_op ().m_dir
== DIR_READ
2650 ? theme::y_arrow_dir::UP
2651 : theme::y_arrow_dir::DOWN
),
2657 /* class access_diagram : public text_art::wrapper_widget. */
2659 /* To hide the implementation details, this is merely a wrapper around
2660 an access_diagram_impl. */
2662 access_diagram::access_diagram (const access_operation
&op
,
2663 diagnostic_event_id_t region_creation_event_id
,
2667 : wrapper_widget (make_unique
<access_diagram_impl
> (op
,
2668 region_creation_event_id
,
2677 #endif /* #if ENABLE_ANALYZER */