Update baseline symbols for hppa-linux.
[official-gcc.git] / gcc / analyzer / access-diagram.cc
bloba51d594b5b2c73919f84a6e9272e838a87cf8ab7
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)
9 any later version.
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/>. */
20 #include "config.h"
21 #define INCLUDE_ALGORITHM
22 #define INCLUDE_MEMORY
23 #define INCLUDE_MAP
24 #define INCLUDE_SET
25 #define INCLUDE_VECTOR
26 #include "system.h"
27 #include "coretypes.h"
28 #include "coretypes.h"
29 #include "tree.h"
30 #include "function.h"
31 #include "basic-block.h"
32 #include "gimple.h"
33 #include "diagnostic.h"
34 #include "intl.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"
43 #if ENABLE_ANALYZER
45 /* Consider this code:
46 int32_t arr[10];
47 arr[10] = x;
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 # +--------------------------------+
63 # |
64 # |
65 # v
66 # +---------+-----------+-----------+ +--------------------------------+
67 # | [0] | ... | [9] | | after valid range |
68 # +---------+-----------+-----------+ | |
69 # | ‘arr’ (type: ‘int32_t[10]’) | | |
70 # +---------------------------------+ +--------------------------------+
71 # |~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~|
72 # | |
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 # +--------------------------------+
92 # |
93 # |
94 # v
95 # +---------+-----------+-----------+ +--------------------------------+
96 # | [0] | ... | [9] | | after valid range |
97 # +---------+-----------+-----------+ | |
98 # | ‘arr’ (type: ‘int32_t[10]’) | | |
99 # +---------------------------------+ +--------------------------------+
100 # |~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~|
101 # | |
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 # +--------------------------------+
112 # | DIRECTION WIDGET
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
122 - DIRECTION WIDGET
123 - ALIGNED CHILD WIDGET: VALID AND INVALID ACCESSES
124 - VALID-VS-INVALID RULER.
126 A more complicated example, given this overflow:
127 char buf[100];
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| |~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
146 17| | |
147 18| +---------+---------+ +----------+----------+
148 19| |capacity: 100 bytes| |overflow of 346 bytes|
149 20| +-------------------+ +---------------------+
151 which is:
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): ~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
169 17| | |
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
176 these subphases:
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;
191 namespace ana {
193 static styled_string
194 fmt_styled_string (style_manager &sm,
195 const char *fmt, ...)
196 ATTRIBUTE_GCC_DIAG(2, 3);
198 static styled_string
199 fmt_styled_string (style_manager &sm,
200 const char *fmt, ...)
202 va_list ap;
203 va_start (ap, fmt);
204 styled_string result
205 = styled_string::from_fmt_va (sm, default_tree_printer, fmt, &ap);
206 va_end (ap);
207 return result;
210 class access_diagram_impl;
211 class bit_to_table_map;
213 static void
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;
219 if (num_bytes == 1)
220 pp_printf (pp, _("%wi byte"), num_bytes.to_uhwi ());
221 else
222 pp_printf (pp, _("%wi bytes"), num_bytes.to_uhwi ());
224 else
226 if (num_bits == 1)
227 pp_printf (pp, _("%wi bit"), num_bits.to_uhwi ());
228 else
229 pp_printf (pp, _("%wi bits"), num_bits.to_uhwi ());
233 static styled_string
234 get_access_size_str (style_manager &sm,
235 const access_operation &op,
236 access_range accessed_range,
237 tree type)
239 bit_size_expr num_bits;
240 if (accessed_range.get_size (op.m_model, &num_bits))
242 if (type)
244 styled_string s;
246 pretty_printer pp;
247 num_bits.print (&pp);
249 if (op.m_dir == DIR_READ)
250 return fmt_styled_string (sm,
251 _("read of %qT (%s)"),
252 type,
253 pp_formatted_text (&pp));
254 else
255 return fmt_styled_string (sm,
256 _("write of %qT (%s)"),
257 type,
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"));
268 else
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"));
278 if (type)
280 if (op.m_dir == DIR_READ)
281 return fmt_styled_string (sm, _("read of %qT"), type);
282 else
283 return fmt_styled_string (sm, _("write of %qT"), type);
286 if (op.m_dir == DIR_READ)
287 return styled_string (sm, _("read"));
288 else
289 return styled_string (sm, _("write"));
292 /* Subroutine of clean_up_for_diagram. */
294 static tree
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);
300 return expr;
303 /* Subroutine of clean_up_for_diagram. */
305 static tree
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));
314 return t;
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,
321 but:
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
330 underlying var name.
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. */
335 tree
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 ());
362 else
363 return fmt_styled_string (sm, concrete_plural_bytes_fmt,
364 concrete_num_bytes.to_uhwi ());
366 else
368 if (concrete_num_bits == 1)
369 return fmt_styled_string (sm, concrete_single_bit_fmt,
370 concrete_num_bits.to_uhwi ());
371 else
372 return fmt_styled_string (sm, concrete_plural_bits_fmt,
373 concrete_num_bits.to_uhwi ());
376 else
378 if (tree bytes_expr = maybe_get_as_bytes ())
379 return fmt_styled_string (sm,
380 symbolic_bytes_fmt,
381 clean_up_for_diagram (bytes_expr));
382 return fmt_styled_string (sm,
383 symbolic_bits_fmt,
384 clean_up_for_diagram (m_num_bits));
388 void
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);
396 else
398 if (tree bytes_expr = maybe_get_as_bytes ())
399 pp_printf (pp, _("%qE bytes"), bytes_expr);
400 else
401 pp_printf (pp, _("%qE bits"), m_num_bits);
405 tree
406 bit_size_expr::maybe_get_as_bytes () const
408 switch (TREE_CODE (m_num_bits))
410 default:
411 break;
412 case INTEGER_CST:
414 const bit_size_t num_bits = wi::to_offset (m_num_bits);
415 if (num_bits % BITS_PER_UNIT != 0)
416 return NULL_TREE;
417 const bit_size_t num_bytes = num_bits / BITS_PER_UNIT;
418 return wide_int_to_tree (size_type_node, num_bytes);
420 break;
421 case PLUS_EXPR:
422 case MINUS_EXPR:
424 bit_size_expr op0
425 = bit_size_expr (TREE_OPERAND (m_num_bits, 0));
426 tree op0_as_bytes = op0.maybe_get_as_bytes ();
427 if (!op0_as_bytes)
428 return NULL_TREE;
429 bit_size_expr op1
430 = bit_size_expr (TREE_OPERAND (m_num_bits, 1));
431 tree op1_as_bytes = op1.maybe_get_as_bytes ();
432 if (!op1_as_bytes)
433 return NULL_TREE;
434 return fold_build2 (TREE_CODE (m_num_bits), size_type_node,
435 op0_as_bytes, op1_as_bytes);
437 break;
438 case MULT_EXPR:
440 bit_size_expr op1
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),
445 op1_as_bytes);
447 break;
449 return NULL_TREE;
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 &reg, region_model_manager *mgr)
471 : m_start (reg.get_offset (mgr)),
472 m_next (reg.get_next_offset (mgr))
476 bool
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);
480 if (!start_expr)
481 return false;
482 tree next_expr = m_next.calc_symbolic_bit_offset (model);
483 if (!next_expr)
484 return false;
485 *out = bit_size_expr (fold_build2 (MINUS_EXPR, size_type_node,
486 next_expr, start_expr));
487 return true;
490 bool
491 access_range::contains_p (const access_range &other) const
493 return (m_start <= other.m_start
494 && other.m_next <= m_next);
497 bool
498 access_range::empty_p () const
500 bit_range concrete_bits (0, 0);
501 if (!as_concrete_bit_range (&concrete_bits))
502 return false;
503 return concrete_bits.empty_p ();
506 void
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);
514 return;
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, ')');
523 DEBUG_FUNCTION void
524 access_range::dump (bool simple) const
526 pretty_printer pp;
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);
531 pp_newline (&pp);
532 pp_flush (&pp);
535 void
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. */
546 access_range
547 access_operation::get_valid_bits () const
549 const svalue *capacity_in_bytes_sval = m_model.get_capacity (m_base_region);
550 return access_range
551 (region_offset::make_concrete (m_base_region, 0),
552 region_offset::make_byte_offset (m_base_region, capacity_in_bytes_sval));
555 access_range
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. */
566 bool
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. */
575 return false;
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);
581 return true;
583 else
585 /* Accessed range is fully before valid range. */
586 *out = actual_bits;
587 return true;
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. */
595 bool
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. */
604 return false;
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);
610 return true;
612 else
614 /* Accessed range is fully after valid range. */
615 *out = actual_bits;
616 return true;
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. */
628 class boundaries
630 public:
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);
641 if (k == kind::HARD)
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 &reg, region_model_manager *mgr, enum kind kind)
653 add (access_range (reg.get_offset (mgr),
654 reg.get_next_offset (mgr)),
655 kind);
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),
669 kind::SOFT);
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);
676 gcc_assert (valid);
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 ())
699 return kind::HARD;
700 else
701 return kind::SOFT;
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 ();
717 private:
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.
727 For example, in:
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||~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
745 17| | |
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
754 public:
755 x_aligned_table_widget (table t,
756 const theme &theme,
757 table_dimension_sizes &col_widths)
758 : m_table (std::move (t)),
759 m_theme (theme),
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,
782 get_top_left (),
783 m_tg,
784 m_theme);
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 ();
794 private:
795 table m_table;
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;
800 table_geometry m_tg;
803 /* A widget for printing arrows between the accessed region
804 and the svalue, showing the direction of the access.
806 For example, in:
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||~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
824 17| | |
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
833 public:
834 direction_widget (const access_diagram_impl &dia_impl,
835 const bit_to_table_map &btm)
836 : leaf_widget (),
837 m_dia_impl (dia_impl),
838 m_btm (btm)
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;
852 private:
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.
861 For example, in:
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||~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
879 17| | |
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
888 public:
889 x_aligned_x_ruler_widget (const access_diagram_impl &dia_impl,
890 const theme &theme,
891 table_dimension_sizes &col_widths)
892 : m_dia_impl (dia_impl),
893 m_theme (theme),
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,
904 styled_string text,
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,
920 get_top_left (),
921 m_theme);
924 private:
925 struct label
927 label (const table::range_t &table_x_range,
928 styled_string text,
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
960 the abvove example:
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
974 public:
975 /* Populate m_table_x_for_bit and m_bit_for_table_x. */
976 void populate (const boundaries &boundaries, logger *logger)
978 LOG_SCOPE (logger);
980 int table_x = 0;
981 std::vector <region_offset> vec_boundaries (boundaries.begin (),
982 boundaries.end ());
984 /* Sort into an order that makes sense. */
985 std::sort (vec_boundaries.begin (),
986 vec_boundaries.end ());
988 if (logger)
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)
1008 table_x += 1;
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);
1017 table_x += 1;
1019 m_num_columns = table_x - 1;
1021 if (logger)
1022 log (*logger);
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 ())
1064 return false;
1065 *out = slot->second;
1066 return true;
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 ();
1088 private:
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. */
1125 class spatial_item
1127 public:
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
1137 the operation. */
1139 class valid_region_spatial_item : public spatial_item
1141 public:
1142 valid_region_spatial_item (const access_operation &op,
1143 diagnostic_event_id_t region_creation_event_id)
1144 : m_op (op),
1145 m_region_creation_event_id (region_creation_event_id)
1148 void add_boundaries (boundaries &out, logger *logger) const final override
1150 LOG_SCOPE (logger);
1151 access_range valid_bits = m_op.get_valid_bits ();
1152 if (logger)
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)
1165 if (logger)
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),
1178 min_idx_sval);
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),
1183 max_idx_sval);
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)))
1199 return;
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);
1206 t.add_row ();
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),
1211 min_idx_sval);
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,
1217 table_y_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),
1225 max_idx_sval);
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,
1233 table_y_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);
1257 styled_string s;
1258 switch (m_op.m_base_region->get_kind ())
1260 default:
1261 s = styled_string (sm, _("region"));
1262 break;
1263 case RK_DECL:
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)",
1269 decl,
1270 TREE_TYPE (decl));
1272 break;
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);
1278 else
1279 s = styled_string (sm, _("heap-allocated buffer"));
1281 break;
1282 case RK_ALLOCA:
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);
1287 else
1288 s = styled_string (sm, _("stack-allocated buffer"));
1290 break;
1291 case RK_STRING:
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));
1299 break;
1301 t.set_cell_span (rect, std::move (s));
1303 return t;
1306 private:
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
1317 public:
1318 accessed_region_spatial_item (const access_operation &op) : m_op (op) {}
1320 void add_boundaries (boundaries &out, logger *logger) const final override
1322 LOG_SCOPE (logger);
1323 access_range actual_bits = m_op.get_actual_bits ();
1324 if (logger)
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)));
1345 return t;
1348 private:
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,
1353 m_op,
1354 accessed_bits,
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
1367 public:
1368 static std::unique_ptr<svalue_spatial_item> make (const access_operation &op,
1369 const svalue &sval,
1370 access_range actual_bits,
1371 const theme &theme);
1373 svalue_spatial_item (const access_operation &op,
1374 const svalue &sval,
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
1381 LOG_SCOPE (logger);
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)));
1394 return t;
1397 protected:
1398 styled_string get_label_string (style_manager &sm) const
1400 tree rep_tree = m_op.m_model.get_representative_tree (&m_sval);
1401 if (rep_tree)
1403 if (TREE_CODE (rep_tree) == SSA_NAME)
1404 rep_tree = SSA_NAME_VAR (rep_tree);
1405 switch (TREE_CODE (rep_tree))
1407 default:
1408 break;
1409 case INTEGER_CST:
1410 return fmt_styled_string (sm, _("write of %<(%T) %E%>"),
1411 TREE_TYPE (rep_tree),
1412 rep_tree);
1414 case PARM_DECL:
1415 case VAR_DECL:
1416 return fmt_styled_string (sm, _("write from %qE (type: %qT)"),
1417 rep_tree,
1418 TREE_TYPE (rep_tree));
1419 break;
1423 const access_range accessed_bits (m_op.get_actual_bits ());
1424 return get_access_size_str (sm,
1425 m_op,
1426 accessed_bits,
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,
1440 with an ellipsis:
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
1468 control characters:
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
1484 public:
1485 string_region_spatial_item (const access_operation &op,
1486 const svalue &sval,
1487 access_range actual_bits,
1488 const string_region &string_reg,
1489 const theme &theme)
1490 : svalue_spatial_item (op, sval, actual_bits),
1491 m_string_reg (string_reg),
1492 m_theme (theme),
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
1503 LOG_SCOPE (logger);
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);
1510 else
1512 byte_range bytes (0, 0);
1513 bool valid = m_actual_bits.as_concrete_byte_range (&bytes);
1514 gcc_assert (valid);
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);
1540 gcc_assert (valid);
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)
1548 add_column_for_byte
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);
1553 if (m_show_utf8)
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;
1558 if (show_unichars)
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),
1566 policy);
1567 while (!dw.done ())
1569 cpp_decoded_char decoded_char;
1570 dw.process_next_codepoint (&decoded_char);
1572 if (!decoded_char.m_valid_ch)
1573 continue;
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 (),
1580 size_in_bytes);
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);
1586 char buf[100];
1587 sprintf (buf, "U+%04x", decoded_char.m_ch);
1588 t.set_cell_span (code_point_table_rect,
1589 styled_string (sm, buf));
1591 if (show_unichars)
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"));
1603 else
1604 t.set_cell_span (character_table_rect,
1605 styled_string (sm, ""));
1610 else
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 (),
1616 byte_idx,
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. */
1630 for (int byte_idx
1631 = (TREE_STRING_LENGTH (string_cst) - m_ellipsis_tail_len);
1632 byte_idx < TREE_STRING_LENGTH (string_cst);
1633 byte_idx++)
1634 add_column_for_byte (t, btm, sm,
1635 byte_idx + bytes.get_start_byte_offset (),
1636 byte_idx,
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)));
1647 return t;
1650 tree get_string_cst () const { return m_string_reg.get_string_cst (); }
1652 private:
1653 bool calc_show_full_string () const
1655 tree string_cst = get_string_cst ();
1656 if (TREE_STRING_LENGTH (string_cst) < m_ellipsis_threshold)
1657 return true;
1658 if (TREE_STRING_LENGTH (string_cst) <
1659 (m_ellipsis_head_len + m_ellipsis_tail_len))
1660 return true;
1661 return false;
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);
1669 byte_idx++)
1671 unsigned char ch = TREE_STRING_POINTER (string_cst)[byte_idx];
1672 if (ch >= 0x80)
1673 return false;
1675 return true;
1678 void add_column_for_byte (table &t, const bit_to_table_map &btm,
1679 style_manager &sm,
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 ()));
1699 char byte_val
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
1710 if (!m_show_utf8)
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);
1718 char buf[100];
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,
1734 const svalue &sval,
1735 access_range actual_bits,
1736 const theme &theme)
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
1750 public:
1751 access_diagram_impl (const access_operation &op,
1752 diagnostic_event_id_t region_creation_event_id,
1753 style_manager &sm,
1754 const theme &theme,
1755 logger *logger)
1756 : m_op (op),
1757 m_region_creation_event_id (region_creation_event_id),
1758 m_sm (sm),
1759 m_theme (theme),
1760 m_logger (logger),
1761 m_invalid (false),
1762 m_valid_region_spatial_item (op, region_creation_event_id),
1763 m_accessed_region_spatial_item (op),
1764 m_btm (),
1765 m_calc_req_size_called (false)
1767 LOG_SCOPE (logger);
1769 if (logger)
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);
1778 if (op.m_sval_hint)
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. */
1789 style valid_style;
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);
1800 if (op.m_sval_hint)
1802 access_range actual_bits = m_op.get_actual_bits ();
1803 m_svalue_spatial_item = svalue_spatial_item::make (m_op,
1804 *op.m_sval_hint,
1805 actual_bits,
1806 m_theme);
1809 /* Two passes:
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 ();
1815 if (logger)
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)
1832 if (logger)
1833 logger->log ("giving up: bad table columns for actual_bits");
1834 m_invalid = true;
1835 return;
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)
1841 if (logger)
1842 logger->log ("giving up: bad table columns for valid_bits");
1843 m_invalid = true;
1844 return;
1848 m_col_widths
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));
1864 else
1866 table t_accessed
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
1887 if (m_invalid)
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 ());
1896 adjust_to_scale();
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; }
1939 private:
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);
1952 return result;
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
1964 example:
1965 +---------+-----------+-----------+---+--------------------------------+
1966 | tc0 | tc1 | tc2 |tc3| tc4 |
1967 +---------+-----------+-----------+---+--------------------------------+
1968 |bytes 0-3|bytes 4-35 |bytes 36-39| | bytes 40-43 |
1969 +---------+-----------+-----------+ +--------------------------------+
1970 which has:
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,
1989 &range_for_column))
1991 pretty_printer pp;
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)));
1999 return t;
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,
2018 table_y, table_h),
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,
2026 table_y, table_h),
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);
2037 if (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);
2043 if (!lower_next)
2045 if (m_logger)
2046 m_logger->log ("failed to get lower_next");
2047 return;
2049 tree upper_start = upper.m_start.calc_symbolic_bit_offset (m_op.m_model);
2050 if (!upper_start)
2052 if (m_logger)
2053 m_logger->log ("failed to get upper_start");
2054 return;
2056 tree num_bits_gap = fold_build2 (MINUS_EXPR,
2057 size_type_node,
2058 upper_start, lower_next);
2059 if (m_logger)
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,
2063 GT_EXPR,
2064 zero,
2065 NULL);
2066 if (ts_gt_zero.is_false ())
2068 if (m_logger)
2069 m_logger->log ("rejecting as not > 0");
2070 return;
2073 bit_size_expr num_bits (num_bits_gap);
2074 styled_string label = num_bits.get_formatted_str (m_sm,
2075 _("%wi bit"),
2076 _("%wi bits"),
2077 _("%wi byte"),
2078 _("%wi bytes"),
2079 _("%qE bits"),
2080 _("%qE bytes"));
2081 w->add_range (m_btm.get_table_x_for_range (access_range (lower.m_next,
2082 upper.m_start)),
2083 std::move (label),
2084 style::id_plain);
2087 styled_string
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. */
2095 true));
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));
2102 return result;
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))
2116 if (m_logger)
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
2124 (m_sm,
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"));
2131 else
2132 label = num_before_bits.get_formatted_str
2133 (m_sm,
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);
2145 else
2147 if (m_logger)
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))
2158 if (m_logger)
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,
2167 _("size: %wi bit"),
2168 _("size: %wi bits"),
2169 _("size: %wi byte"),
2170 _("size: %wi bytes"),
2171 _("size: %qE bits"),
2172 _("size: %qE bytes"));
2173 else
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 ()),
2182 std::move (label),
2183 m_valid_style_id);
2186 access_range invalid_after_bits;
2187 if (m_op.maybe_get_invalid_after_bits (&invalid_after_bits))
2189 if (got_valid_bits)
2190 maybe_add_gap (w, valid_bits, invalid_after_bits);
2192 if (m_logger)
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
2201 (m_sm,
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"));
2208 else
2209 label = num_after_bits.get_formatted_str
2210 (m_sm,
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);
2222 else
2224 if (m_logger)
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.
2234 See e.g.:
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,
2247 &range_for_column))
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;
2255 else
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
2271 of the table. */
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)
2283 if (m_logger)
2284 m_logger->log ("bailing out: total_width=%i ,>= max_width (%i)\n",
2285 total_width, max_width);
2286 return false;
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];
2294 if (bit_size > 0)
2295 canvas_w_per_bit[table_x]
2296 = (m_col_widths->m_requirements[table_x] * fixed_point) / bit_size;
2297 else
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;
2307 if (m_logger)
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;
2317 logger *m_logger;
2318 /* In lieu of being able to throw exceptions, a flag to mark this object
2319 as "invalid". */
2320 bool m_invalid;
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
2340 column widths. */
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;
2347 x_ruler
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);
2360 return r;
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
2367 accessed. */
2369 void
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))
2384 continue;
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,
2392 table_x + 1));
2393 const int canvas_x = x_canvas_range.get_midpoint ();
2394 m_dia_impl.get_theme ().paint_y_arrow
2395 (canvas,
2396 canvas_x,
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),
2401 style_id);
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,
2413 style_manager &sm,
2414 const theme &theme,
2415 logger *logger)
2416 : wrapper_widget (make_unique <access_diagram_impl> (op,
2417 region_creation_event_id,
2419 theme,
2420 logger))
2424 } // namespace ana
2426 #endif /* #if ENABLE_ANALYZER */