analyzer: Fix up some -Wformat* warnings
[official-gcc.git] / gcc / analyzer / access-diagram.cc
blob2836308c01999c863c99d41c1301566f6a797288
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)
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-core.h"
34 #include "diagnostic.h"
35 #include "intl.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"
44 #if ENABLE_ANALYZER
46 /* Consider this code:
47 int32_t arr[10];
48 arr[10] = x;
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 # +--------------------------------+
64 # |
65 # |
66 # v
67 # +---------+-----------+-----------+ +--------------------------------+
68 # | [0] | ... | [9] | | after valid range |
69 # +---------+-----------+-----------+ | |
70 # | ‘arr’ (type: ‘int32_t[10]’) | | |
71 # +---------------------------------+ +--------------------------------+
72 # |~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~|
73 # | |
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 # +--------------------------------+
93 # |
94 # |
95 # v
96 # +---------+-----------+-----------+ +--------------------------------+
97 # | [0] | ... | [9] | | after valid range |
98 # +---------+-----------+-----------+ | |
99 # | ‘arr’ (type: ‘int32_t[10]’) | | |
100 # +---------------------------------+ +--------------------------------+
101 # |~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~|
102 # | |
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 # +--------------------------------+
113 # | DIRECTION WIDGET
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
123 - DIRECTION WIDGET
124 - ALIGNED CHILD WIDGET: VALID AND INVALID ACCESSES
125 - VALID-VS-INVALID RULER.
127 A more complicated example, given this overflow:
128 char buf[100];
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| |~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
147 17| | |
148 18| +---------+---------+ +----------+----------+
149 19| |capacity: 100 bytes| |overflow of 346 bytes|
150 20| +-------------------+ +---------------------+
152 which is:
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): ~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
170 17| | |
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
177 these subphases:
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;
192 namespace ana {
194 static styled_string
195 fmt_styled_string (style_manager &sm,
196 const char *fmt, ...)
197 ATTRIBUTE_GCC_DIAG(2, 3);
199 static styled_string
200 fmt_styled_string (style_manager &sm,
201 const char *fmt, ...)
203 va_list ap;
204 va_start (ap, fmt);
205 styled_string result
206 = styled_string::from_fmt_va (sm, default_tree_printer, fmt, &ap);
207 va_end (ap);
208 return result;
211 class access_diagram_impl;
212 class bit_to_table_map;
214 static void
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;
220 if (num_bytes == 1)
221 pp_printf (pp, _("%wi byte"), num_bytes.to_uhwi ());
222 else
223 pp_printf (pp, _("%wi bytes"), num_bytes.to_uhwi ());
225 else
227 if (num_bits == 1)
228 pp_printf (pp, _("%wi bit"), num_bits.to_uhwi ());
229 else
230 pp_printf (pp, _("%wi bits"), num_bits.to_uhwi ());
234 static styled_string
235 get_access_size_str (style_manager &sm,
236 const access_operation &op,
237 access_range accessed_range,
238 tree type)
240 bit_size_expr num_bits;
241 if (accessed_range.get_size (op.m_model, &num_bits))
243 if (type)
245 styled_string s;
247 pretty_printer pp;
248 num_bits.print (&pp);
250 if (op.m_dir == DIR_READ)
251 return fmt_styled_string (sm,
252 _("read of %qT (%s)"),
253 type,
254 pp_formatted_text (&pp));
255 else
256 return fmt_styled_string (sm,
257 _("write of %qT (%s)"),
258 type,
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"));
269 else
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"));
279 if (type)
281 if (op.m_dir == DIR_READ)
282 return fmt_styled_string (sm, _("read of %qT"), type);
283 else
284 return fmt_styled_string (sm, _("write of %qT"), type);
287 if (op.m_dir == DIR_READ)
288 return styled_string (sm, _("read"));
289 else
290 return styled_string (sm, _("write"));
293 /* Subroutine of clean_up_for_diagram. */
295 static tree
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);
301 return expr;
304 /* Subroutine of clean_up_for_diagram. */
306 static tree
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));
315 return t;
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,
322 but:
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
331 underlying var name.
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. */
336 tree
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 ());
363 else
364 return fmt_styled_string (sm, concrete_plural_bytes_fmt,
365 concrete_num_bytes.to_uhwi ());
367 else
369 if (concrete_num_bits == 1)
370 return fmt_styled_string (sm, concrete_single_bit_fmt,
371 concrete_num_bits.to_uhwi ());
372 else
373 return fmt_styled_string (sm, concrete_plural_bits_fmt,
374 concrete_num_bits.to_uhwi ());
377 else
379 if (tree bytes_expr = maybe_get_as_bytes ())
380 return fmt_styled_string (sm,
381 symbolic_bytes_fmt,
382 clean_up_for_diagram (bytes_expr));
383 return fmt_styled_string (sm,
384 symbolic_bits_fmt,
385 clean_up_for_diagram (m_num_bits));
389 void
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);
397 else
399 if (tree bytes_expr = maybe_get_as_bytes ())
400 pp_printf (pp, _("%qE bytes"), bytes_expr);
401 else
402 pp_printf (pp, _("%qE bits"), m_num_bits);
406 tree
407 bit_size_expr::maybe_get_as_bytes () const
409 switch (TREE_CODE (m_num_bits))
411 default:
412 break;
413 case INTEGER_CST:
415 const bit_size_t num_bits = wi::to_offset (m_num_bits);
416 if (num_bits % BITS_PER_UNIT != 0)
417 return NULL_TREE;
418 const bit_size_t num_bytes = num_bits / BITS_PER_UNIT;
419 return wide_int_to_tree (size_type_node, num_bytes);
421 break;
422 case PLUS_EXPR:
423 case MINUS_EXPR:
425 bit_size_expr op0
426 = bit_size_expr (TREE_OPERAND (m_num_bits, 0));
427 tree op0_as_bytes = op0.maybe_get_as_bytes ();
428 if (!op0_as_bytes)
429 return NULL_TREE;
430 bit_size_expr op1
431 = bit_size_expr (TREE_OPERAND (m_num_bits, 1));
432 tree op1_as_bytes = op1.maybe_get_as_bytes ();
433 if (!op1_as_bytes)
434 return NULL_TREE;
435 return fold_build2 (TREE_CODE (m_num_bits), size_type_node,
436 op0_as_bytes, op1_as_bytes);
438 break;
439 case MULT_EXPR:
441 bit_size_expr op1
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),
446 op1_as_bytes);
448 break;
450 return NULL_TREE;
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 &reg, region_model_manager *mgr)
472 : m_start (reg.get_offset (mgr)),
473 m_next (reg.get_next_offset (mgr))
477 bool
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);
481 if (!start_expr)
482 return false;
483 tree next_expr = m_next.calc_symbolic_bit_offset (model);
484 if (!next_expr)
485 return false;
486 *out = bit_size_expr (fold_build2 (MINUS_EXPR, size_type_node,
487 next_expr, start_expr));
488 return true;
491 bool
492 access_range::contains_p (const access_range &other) const
494 return (m_start <= other.m_start
495 && other.m_next <= m_next);
498 bool
499 access_range::empty_p () const
501 bit_range concrete_bits (0, 0);
502 if (!as_concrete_bit_range (&concrete_bits))
503 return false;
504 return concrete_bits.empty_p ();
507 void
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);
515 return;
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, ')');
524 DEBUG_FUNCTION void
525 access_range::dump (bool simple) const
527 pretty_printer pp;
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);
532 pp_newline (&pp);
533 pp_flush (&pp);
536 void
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. */
547 access_range
548 access_operation::get_valid_bits () const
550 const svalue *capacity_in_bytes_sval = m_model.get_capacity (m_base_region);
551 return access_range
552 (region_offset::make_concrete (m_base_region, 0),
553 region_offset::make_byte_offset (m_base_region, capacity_in_bytes_sval));
556 access_range
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. */
567 bool
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. */
576 return false;
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);
582 return true;
584 else
586 /* Accessed range is fully before valid range. */
587 *out = actual_bits;
588 return true;
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. */
596 bool
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. */
605 return false;
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);
611 return true;
613 else
615 /* Accessed range is fully after valid range. */
616 *out = actual_bits;
617 return true;
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. */
629 class boundaries
631 public:
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);
642 if (k == kind::HARD)
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);
650 if (m_logger)
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)
657 ? "HARD" : "soft");
658 m_logger->end_log_line ();
662 void add (const region &reg, region_model_manager *mgr, enum kind kind)
664 add (access_range (reg.get_offset (mgr),
665 reg.get_next_offset (mgr)),
666 kind);
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),
680 kind::SOFT);
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);
687 gcc_assert (valid);
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 ())
710 return kind::HARD;
711 else
712 return kind::SOFT;
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 ())
736 continue;
737 byte_offset_t byte;
738 if (!offset.get_concrete_byte_offset (&byte))
739 continue;
740 if (byte < min_offset)
741 continue;
742 if (byte > max_offset)
743 continue;
744 result.push_back (offset);
746 return result;
749 private:
750 const region &m_base_reg;
751 logger *m_logger;
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.
760 For example, in:
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||~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
778 17| | |
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
787 public:
788 x_aligned_table_widget (table t,
789 const theme &theme,
790 table_dimension_sizes &col_widths)
791 : m_table (std::move (t)),
792 m_theme (theme),
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,
815 get_top_left (),
816 m_tg,
817 m_theme);
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 ();
827 private:
828 table m_table;
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;
833 table_geometry m_tg;
836 /* A widget for printing arrows between the accessed region
837 and the svalue, showing the direction of the access.
839 For example, in:
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||~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
857 17| | |
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
866 public:
867 direction_widget (const access_diagram_impl &dia_impl,
868 const bit_to_table_map &btm)
869 : leaf_widget (),
870 m_dia_impl (dia_impl),
871 m_btm (btm)
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;
885 private:
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.
894 For example, in:
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||~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
912 17| | |
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
921 public:
922 x_aligned_x_ruler_widget (const access_diagram_impl &dia_impl,
923 const theme &theme)
924 : m_dia_impl (dia_impl),
925 m_theme (theme)
929 const char *get_desc () const override
931 return "x_aligned_ruler_widget";
934 void add_range (const table::range_t &x_range,
935 styled_string text,
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,
951 get_top_left (),
952 m_theme);
955 private:
956 struct label
958 label (const table::range_t &table_x_range,
959 styled_string text,
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
990 the abvove example:
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
1004 public:
1005 /* Populate m_table_x_for_bit and m_bit_for_table_x. */
1006 void populate (const boundaries &boundaries, logger *logger)
1008 LOG_SCOPE (logger);
1010 int table_x = 0;
1011 std::vector <region_offset> vec_boundaries (boundaries.begin (),
1012 boundaries.end ());
1014 /* Sort into an order that makes sense. */
1015 std::sort (vec_boundaries.begin (),
1016 vec_boundaries.end ());
1018 if (logger)
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)
1038 table_x += 1;
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);
1047 table_x += 1;
1049 m_num_columns = table_x - 1;
1051 if (logger)
1052 log (*logger);
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 ())
1094 return false;
1095 *out = slot->second;
1096 return true;
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;
1129 private:
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. */
1155 class spatial_item
1157 public:
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
1169 public:
1170 enum class kind
1172 WRITTEN,
1173 EXISTING
1175 protected:
1176 svalue_spatial_item (const svalue &sval,
1177 access_range bits,
1178 enum kind kind)
1179 : m_sval (sval), m_bits (bits), m_kind (kind)
1183 const svalue &m_sval;
1184 access_range m_bits;
1185 enum kind m_kind;
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
1195 public:
1196 compound_svalue_spatial_item (const compound_svalue &sval,
1197 const access_range &bits,
1198 enum kind kind,
1199 const theme &theme)
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,
1216 range,
1217 theme))
1218 m_children.push_back (std::move (child));
1223 void add_boundaries (boundaries &out, logger *logger) const final override
1225 LOG_SCOPE (logger);
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;
1234 int max_rows = 0;
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));
1245 return t;
1248 private:
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. */
1256 static void
1257 add_ellipsis_to_gaps (table &t,
1258 style_manager &sm,
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 ())))
1270 table_x++;
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 ())))
1279 table_x++;
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
1285 the operation. */
1287 class valid_region_spatial_item : public spatial_item
1289 public:
1290 valid_region_spatial_item (const access_operation &op,
1291 diagnostic_event_id_t region_creation_event_id,
1292 const theme &theme)
1293 : m_op (op),
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 (),
1300 theme))
1304 void add_boundaries (boundaries &out, logger *logger) const final override
1306 LOG_SCOPE (logger);
1307 m_boundaries = &out;
1308 access_range valid_bits = m_op.get_valid_bits ();
1309 if (logger)
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)
1320 if (logger)
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)
1334 if (logger)
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),
1347 min_idx_sval);
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),
1352 max_idx_sval);
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)))
1369 return;
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);
1375 t.add_row ();
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 ())
1397 continue;
1398 byte_offset_t byte;
1399 if (!offset.get_concrete_byte_offset (&byte))
1400 continue;
1401 table::range_t table_x_range (table_x, table_x + 1);
1402 t.maybe_set_cell_span (table::rect_t (table_x_range,
1403 table_y_range),
1404 fmt_styled_string (sm, "[%wi]",
1405 byte.to_shwi ()));
1409 add_ellipsis_to_gaps (t, sm,
1410 table::range_t (min_x_range.get_next (),
1411 max_x_range.get_min ()),
1412 table_y_range);
1415 table::range_t
1416 maybe_add_array_index_to_table (table &t,
1417 const bit_to_table_map &btm,
1418 style_manager &sm,
1419 const table::range_t table_y_range,
1420 tree idx_cst) const
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),
1428 idx_sval);
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,
1434 table_y_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);
1463 styled_string s;
1464 switch (m_op.m_base_region->get_kind ())
1466 default:
1467 s = styled_string (sm, _("region"));
1468 break;
1469 case RK_DECL:
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)",
1475 decl,
1476 TREE_TYPE (decl));
1478 break;
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);
1484 else
1485 s = styled_string (sm, _("heap-allocated buffer"));
1487 break;
1488 case RK_ALLOCA:
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);
1493 else
1494 s = styled_string (sm, _("stack-allocated buffer"));
1496 break;
1497 case RK_STRING:
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));
1505 break;
1507 t.set_cell_span (rect, std::move (s));
1509 return t;
1512 private:
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
1526 public:
1527 accessed_region_spatial_item (const access_operation &op) : m_op (op) {}
1529 void add_boundaries (boundaries &out, logger *logger) const final override
1531 LOG_SCOPE (logger);
1532 access_range actual_bits = m_op.get_actual_bits ();
1533 if (logger)
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)));
1554 return t;
1557 private:
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,
1562 m_op,
1563 accessed_bits,
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
1576 public:
1577 written_svalue_spatial_item (const access_operation &op,
1578 const svalue &sval,
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
1585 LOG_SCOPE (logger);
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)));
1598 return t;
1601 protected:
1602 styled_string get_label_string (style_manager &sm) const
1604 tree rep_tree = m_op.m_model.get_representative_tree (&m_sval);
1605 if (rep_tree)
1607 if (TREE_CODE (rep_tree) == SSA_NAME)
1608 rep_tree = SSA_NAME_VAR (rep_tree);
1609 switch (TREE_CODE (rep_tree))
1611 default:
1612 break;
1613 case INTEGER_CST:
1614 return fmt_styled_string (sm, _("write of %<(%T) %E%>"),
1615 TREE_TYPE (rep_tree),
1616 rep_tree);
1618 case PARM_DECL:
1619 case VAR_DECL:
1620 return fmt_styled_string (sm, _("write from %qE (type: %qT)"),
1621 rep_tree,
1622 TREE_TYPE (rep_tree));
1623 break;
1627 const access_range accessed_bits (m_op.get_actual_bits ());
1628 return get_access_size_str (sm,
1629 m_op,
1630 accessed_bits,
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,
1644 with an ellipsis:
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
1672 control characters:
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
1688 public:
1689 string_literal_spatial_item (const svalue &sval,
1690 access_range actual_bits,
1691 const string_region &string_reg,
1692 const theme &theme,
1693 enum kind kind)
1694 : svalue_spatial_item (sval, actual_bits, kind),
1695 m_string_reg (string_reg),
1696 m_theme (theme),
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
1707 LOG_SCOPE (logger);
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);
1716 else
1718 byte_range bytes (0, 0);
1719 bool valid = m_bits.as_concrete_byte_range (&bytes);
1720 gcc_assert (valid);
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
1742 ? t.add_row ()
1743 : -1);
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);
1748 gcc_assert (valid);
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)
1756 add_column_for_byte
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);
1761 if (m_show_utf8)
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;
1766 if (show_unichars)
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),
1774 policy);
1775 while (!dw.done ())
1777 cpp_decoded_char decoded_char;
1778 dw.process_next_codepoint (&decoded_char);
1780 if (!decoded_char.m_valid_ch)
1781 continue;
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 (),
1788 size_in_bytes);
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);
1794 char buf[100];
1795 sprintf (buf, "U+%04x", decoded_char.m_ch);
1796 t.set_cell_span (code_point_table_rect,
1797 styled_string (sm, buf));
1799 if (show_unichars)
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"));
1811 else
1812 t.set_cell_span (character_table_rect,
1813 styled_string (sm, ""));
1818 else
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 (),
1824 byte_idx,
1825 byte_idx_table_y, byte_val_table_y);
1827 /* Ellipsis. */
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. */
1841 for (int byte_idx
1842 = (TREE_STRING_LENGTH (string_cst) - m_ellipsis_tail_len);
1843 byte_idx < TREE_STRING_LENGTH (string_cst);
1844 byte_idx++)
1845 add_column_for_byte (t, btm, sm,
1846 byte_idx + bytes.get_start_byte_offset (),
1847 byte_idx,
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)));
1861 return t;
1864 tree get_string_cst () const { return m_string_reg.get_string_cst (); }
1866 private:
1867 bool calc_show_full_string () const
1869 tree string_cst = get_string_cst ();
1870 if (TREE_STRING_LENGTH (string_cst) < m_ellipsis_threshold)
1871 return true;
1872 if (TREE_STRING_LENGTH (string_cst) <
1873 (m_ellipsis_head_len + m_ellipsis_tail_len))
1874 return true;
1875 return false;
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);
1883 byte_idx++)
1885 unsigned char ch = TREE_STRING_POINTER (string_cst)[byte_idx];
1886 if (ch >= 0x80)
1887 return false;
1889 return true;
1892 void add_column_for_byte (table &t, const bit_to_table_map &btm,
1893 style_manager &sm,
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 ()));
1913 char byte_val
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
1924 if (!m_show_utf8)
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);
1932 char buf[100];
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,
1948 const svalue &sval,
1949 access_range actual_bits,
1950 const theme &theme)
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>
1956 (sval, actual_bits,
1957 *string_reg, theme,
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,
1965 const theme &theme)
1967 if (!sval)
1968 return nullptr;
1970 switch (sval->get_kind ())
1972 default:
1973 return nullptr;
1975 case SK_INITIAL:
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>
1981 (*sval, bits,
1982 *string_reg, theme,
1983 svalue_spatial_item::kind::EXISTING);
1984 return nullptr;
1987 case SK_COMPOUND:
1988 return make_unique<compound_svalue_spatial_item>
1989 (*((const compound_svalue *)sval),
1990 bits,
1991 svalue_spatial_item::kind::EXISTING,
1992 theme);
1996 /* Widget subclass implementing access diagrams. */
1998 class access_diagram_impl : public vbox_widget
2000 public:
2001 access_diagram_impl (const access_operation &op,
2002 diagnostic_event_id_t region_creation_event_id,
2003 style_manager &sm,
2004 const theme &theme,
2005 logger *logger)
2006 : m_op (op),
2007 m_region_creation_event_id (region_creation_event_id),
2008 m_sm (sm),
2009 m_theme (theme),
2010 m_logger (logger),
2011 m_invalid (false),
2012 m_valid_region_spatial_item (op, region_creation_event_id, theme),
2013 m_accessed_region_spatial_item (op),
2014 m_btm (),
2015 m_calc_req_size_called (false)
2017 LOG_SCOPE (logger);
2019 if (logger)
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);
2028 if (op.m_sval_hint)
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. */
2039 style valid_style;
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);
2050 if (op.m_sval_hint)
2052 access_range actual_bits = m_op.get_actual_bits ();
2053 m_written_svalue_spatial_item
2054 = make_written_svalue_spatial_item (m_op,
2055 *op.m_sval_hint,
2056 actual_bits,
2057 m_theme);
2060 /* Two passes:
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 ();
2066 if (logger)
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)
2083 if (logger)
2084 logger->log ("giving up: bad table columns for actual_bits");
2085 m_invalid = true;
2086 return;
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)
2092 if (logger)
2093 logger->log ("giving up: bad table columns for valid_bits");
2094 m_invalid = true;
2095 return;
2099 m_col_widths
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));
2115 else
2117 table t_accessed
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
2138 if (m_invalid)
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 ());
2147 adjust_to_scale();
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; }
2190 private:
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);
2203 return result;
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
2215 example:
2216 +---------+-----------+-----------+---+--------------------------------+
2217 | tc0 | tc1 | tc2 |tc3| tc4 |
2218 +---------+-----------+-----------+---+--------------------------------+
2219 |bytes 0-3|bytes 4-35 |bytes 36-39| | bytes 40-43 |
2220 +---------+-----------+-----------+ +--------------------------------+
2221 which has:
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,
2240 &range_for_column))
2242 pretty_printer pp;
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)));
2250 return t;
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,
2269 table_y, table_h),
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,
2277 table_y, table_h),
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);
2288 if (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);
2294 if (!lower_next)
2296 if (m_logger)
2297 m_logger->log ("failed to get lower_next");
2298 return;
2300 tree upper_start = upper.m_start.calc_symbolic_bit_offset (m_op.m_model);
2301 if (!upper_start)
2303 if (m_logger)
2304 m_logger->log ("failed to get upper_start");
2305 return;
2307 tree num_bits_gap = fold_build2 (MINUS_EXPR,
2308 size_type_node,
2309 upper_start, lower_next);
2310 if (m_logger)
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,
2314 GT_EXPR,
2315 zero,
2316 NULL);
2317 if (ts_gt_zero.is_false ())
2319 if (m_logger)
2320 m_logger->log ("rejecting as not > 0");
2321 return;
2324 bit_size_expr num_bits (num_bits_gap);
2325 styled_string label = num_bits.get_formatted_str (m_sm,
2326 _("%wi bit"),
2327 _("%wi bits"),
2328 _("%wi byte"),
2329 _("%wi bytes"),
2330 _("%qE bits"),
2331 _("%qE bytes"));
2332 w->add_range (m_btm.get_table_x_for_range (access_range (lower.m_next,
2333 upper.m_start)),
2334 std::move (label),
2335 style::id_plain);
2338 styled_string
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. */
2346 true));
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));
2353 return result;
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))
2367 if (m_logger)
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
2375 (m_sm,
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"));
2382 else
2383 label = num_before_bits.get_formatted_str
2384 (m_sm,
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);
2396 else
2398 if (m_logger)
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))
2409 if (m_logger)
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,
2418 _("size: %wi bit"),
2419 _("size: %wi bits"),
2420 _("size: %wi byte"),
2421 _("size: %wi bytes"),
2422 _("size: %qE bits"),
2423 _("size: %qE bytes"));
2424 else
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 ()),
2433 std::move (label),
2434 m_valid_style_id);
2437 access_range invalid_after_bits;
2438 if (m_op.maybe_get_invalid_after_bits (&invalid_after_bits))
2440 if (got_valid_bits)
2441 maybe_add_gap (w, valid_bits, invalid_after_bits);
2443 if (m_logger)
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
2452 (m_sm,
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"));
2459 else
2460 label = num_after_bits.get_formatted_str
2461 (m_sm,
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);
2473 else
2475 if (m_logger)
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.
2485 See e.g.:
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,
2498 &range_for_column))
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;
2506 else
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
2522 of the table. */
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)
2534 if (m_logger)
2535 m_logger->log ("bailing out: total_width=%i ,>= max_width (%i)\n",
2536 total_width, max_width);
2537 return false;
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];
2545 if (bit_size > 0)
2546 canvas_w_per_bit[table_x]
2547 = (m_col_widths->m_requirements[table_x] * fixed_point) / bit_size;
2548 else
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;
2558 if (m_logger)
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;
2568 logger *m_logger;
2569 /* In lieu of being able to throw exceptions, a flag to mark this object
2570 as "invalid". */
2571 bool m_invalid;
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
2591 column widths. */
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;
2598 x_ruler
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);
2611 return r;
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
2618 accessed. */
2620 void
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))
2635 continue;
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,
2643 table_x + 1));
2644 const int canvas_x = x_canvas_range.get_midpoint ();
2645 m_dia_impl.get_theme ().paint_y_arrow
2646 (canvas,
2647 canvas_x,
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),
2652 style_id);
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,
2664 style_manager &sm,
2665 const theme &theme,
2666 logger *logger)
2667 : wrapper_widget (make_unique <access_diagram_impl> (op,
2668 region_creation_event_id,
2670 theme,
2671 logger))
2675 } // namespace ana
2677 #endif /* #if ENABLE_ANALYZER */