libstdc++: Simplify std::any to fix -Wdeprecated-declarations warning
[official-gcc.git] / gcc / analyzer / access-diagram.cc
blobcb5b656c164a407baf339eeb68bcbf31e8d35938
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"
43 #include "analyzer/analyzer-selftests.h"
45 #if ENABLE_ANALYZER
47 /* Consider this code:
48 int32_t arr[10];
49 arr[10] = x;
50 where we've emitted a buffer overflow diagnostic like this:
51 out-of-bounds write from byte 40 till byte 43 but 'arr' ends at byte 40
53 We want to emit a diagram that visualizes:
54 - the spatial relationship between the valid region to access, versus
55 the region that was actually accessed: does it overlap, was it touching,
56 close, or far away? Was it before or after in memory? What are the
57 relative sizes involved?
58 - the direction of the access (read vs write)
60 The following code supports emitting diagrams similar to the following:
62 # +--------------------------------+
63 # |write from ‘x’ (type: ‘int32_t’)|
64 # +--------------------------------+
65 # |
66 # |
67 # v
68 # +---------+-----------+-----------+ +--------------------------------+
69 # | [0] | ... | [9] | | after valid range |
70 # +---------+-----------+-----------+ | |
71 # | ‘arr’ (type: ‘int32_t[10]’) | | |
72 # +---------------------------------+ +--------------------------------+
73 # |~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~|
74 # | |
75 # +---------+--------+ +---------+---------+
76 # |capacity: 40 bytes| |overflow of 4 bytes|
77 # +------------------+ +-------------------+
79 where the diagram is laid out via table columns where each table column
80 represents either a range of bits/bytes, or is a spacing column (to highlight
81 the boundary between valid vs invalid accesses). The table columns can be
82 seen via -fanalyzer-debug-text-art. For example, here there are 5 table
83 columns ("tc0" through "tc4"):
85 # +---------+-----------+-----------+---+--------------------------------+
86 # | tc0 | tc1 | tc2 |tc3| tc4 |
87 # +---------+-----------+-----------+---+--------------------------------+
88 # |bytes 0-3|bytes 4-35 |bytes 36-39| | bytes 40-43 |
89 # +---------+-----------+-----------+ +--------------------------------+
91 # +--------------------------------+
92 # |write from ‘x’ (type: ‘int32_t’)|
93 # +--------------------------------+
94 # |
95 # |
96 # v
97 # +---------+-----------+-----------+ +--------------------------------+
98 # | [0] | ... | [9] | | after valid range |
99 # +---------+-----------+-----------+ | |
100 # | ‘arr’ (type: ‘int32_t[10]’) | | |
101 # +---------------------------------+ +--------------------------------+
102 # |~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~|
103 # | |
104 # +---------+--------+ +---------+---------+
105 # |capacity: 40 bytes| |overflow of 4 bytes|
106 # +------------------+ +-------------------+
108 The diagram is built up from the following:
110 # +--------------------------------+
111 # | ITEM FOR SVALUE/ACCESSED REGION|
112 # +--------------------------------+
114 # | DIRECTION WIDGET
116 # +---------------------------------+ +--------------------------------+
117 # | VALID REGION | | INVALID ACCESS |
118 # +---------------------------------+ +--------------------------------+
120 # | VALID-VS-INVALID RULER |
122 i.e. a vbox_widget containing 4 child widgets laid out vertically:
123 - ALIGNED CHILD WIDGET: ITEM FOR SVALUE/ACCESSED REGION
124 - DIRECTION WIDGET
125 - ALIGNED CHILD WIDGET: VALID AND INVALID ACCESSES
126 - VALID-VS-INVALID RULER.
128 A more complicated example, given this overflow:
129 char buf[100];
130 strcpy (buf, LOREM_IPSUM);
132 01| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
133 02| |[0]|[1]|[2]|[3]|[4]|[5]| ... |[440]|[441]|[442]|[443]|[444]|[445]|
134 03| +---+---+---+---+---+---+ +-----+-----+-----+-----+-----+-----+
135 04| |'L'|'o'|'r'|'e'|'m'|' '| | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
136 05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
137 06| | string literal (type: 'char[446]') |
138 07| +----------------------------------------------------------------------+
139 08| | | | | | | | | | | | | | | |
140 09| | | | | | | | | | | | | | | |
141 10| v v v v v v v v v v v v v v v
142 11| +---+---------------------+----++--------------------------------------+
143 12| |[0]| ... |[99]|| after valid range |
144 13| +---+---------------------+----+| |
145 14| | 'buf' (type: 'char[100]') || |
146 15| +------------------------------++--------------------------------------+
147 16| |~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
148 17| | |
149 18| +---------+---------+ +----------+----------+
150 19| |capacity: 100 bytes| |overflow of 346 bytes|
151 20| +-------------------+ +---------------------+
153 which is:
155 01| ALIGNED CHILD WIDGET (lines 01-07): (string_region_spatial_item)-+-----+
156 02| |[0]|[1]|[2]|[3]|[4]|[5]| ... |[440]|[441]|[442]|[443]|[444]|[445]|
157 03| +---+---+---+---+---+---+ +-----+-----+-----+-----+-----+-----+
158 04| |'L'|'o'|'r'|'e'|'m'|' '| | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
159 05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
160 06| | string literal (type: 'char[446]') |
161 07| +----------------------------------------------------------------------+
162 08| DIRECTION WIDGET (lines 08-10) | | | | | | |
163 09| | | | | | | | | | | | | | | |
164 10| v v v v v v v v v v v v v v v
165 11| ALIGNED CHILD WIDGET (lines 11-15)-------------------------------------+
166 12| VALID REGION ... |[99]|| INVALID ACCESS |
167 13| +---+---------------------+----+| |
168 14| | 'buf' (type: 'char[100]') || |
169 15| +------------------------------++--------------------------------------+
170 16| VALID-VS-INVALID RULER (lines 16-20): ~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
171 17| | |
172 18| +---------+---------+ +----------+----------+
173 19| |capacity: 100 bytes| |overflow of 346 bytes|
174 20| +-------------------+ +---------------------+
176 We build the diagram in several phases:
177 - (1) we construct an access_diagram_impl widget. Within the ctor, we have
178 these subphases:
179 - (1.1) find all of the boundaries of interest
180 - (1.2) use the boundaries to build a bit_table_map, associating bit ranges
181 with table columns (e.g. "byte 0 is column 0, bytes 1-98 are column 2" etc)
182 - (1.3) create child widgets that share this table-based geometry
183 - (2) ask the widget for its size request
184 - (2.1) column widths and row heights for the table are computed by
185 access_diagram_impl::calc_req_size
186 - (2.2) child widgets request sizes based on these widths/heights
187 - (3) create a canvas of the appropriate size
188 - (4) paint the widget hierarchy to the canvas. */
191 using namespace text_art;
193 namespace ana {
195 static styled_string
196 fmt_styled_string (style_manager &sm,
197 const char *fmt, ...)
198 ATTRIBUTE_GCC_DIAG(2, 3);
200 static styled_string
201 fmt_styled_string (style_manager &sm,
202 const char *fmt, ...)
204 va_list ap;
205 va_start (ap, fmt);
206 styled_string result
207 = styled_string::from_fmt_va (sm, default_tree_printer, fmt, &ap);
208 va_end (ap);
209 return result;
212 class access_diagram_impl;
213 class bit_to_table_map;
215 static void
216 pp_bit_size_t (pretty_printer *pp, bit_size_t num_bits)
218 if (num_bits % BITS_PER_UNIT == 0)
220 byte_size_t num_bytes = num_bits / BITS_PER_UNIT;
221 if (num_bytes == 1)
222 pp_printf (pp, _("%wi byte"), num_bytes.to_uhwi ());
223 else
224 pp_printf (pp, _("%wi bytes"), num_bytes.to_uhwi ());
226 else
228 if (num_bits == 1)
229 pp_printf (pp, _("%wi bit"), num_bits.to_uhwi ());
230 else
231 pp_printf (pp, _("%wi bits"), num_bits.to_uhwi ());
235 static styled_string
236 get_access_size_str (style_manager &sm,
237 const access_operation &op,
238 access_range accessed_range,
239 tree type)
241 bit_size_expr num_bits (accessed_range.get_size (op.m_model.get_manager ()));
242 if (type)
244 styled_string s;
245 pretty_printer pp;
246 pp_format_decoder (&pp) = default_tree_printer;
247 if (num_bits.maybe_print_for_user (&pp, op.m_model))
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));
261 if (op.m_dir == DIR_READ)
263 if (auto p
264 = num_bits.maybe_get_formatted_str (sm, op.m_model,
265 _("read of %wi bit"),
266 _("read of %wi bits"),
267 _("read of %wi byte"),
268 _("read of %wi bytes"),
269 _("read of %qs bits"),
270 _("read of %qs bytes")))
271 return std::move (*p.get ());
273 else
275 if (auto p
276 = num_bits.maybe_get_formatted_str (sm, op.m_model,
277 _("write of %wi bit"),
278 _("write of %wi bits"),
279 _("write of %wi byte"),
280 _("write of %wi bytes"),
281 _("write of %qs bits"),
282 _("write of %qs bytes")))
283 return std::move (*p.get ());
286 if (type)
288 if (op.m_dir == DIR_READ)
289 return fmt_styled_string (sm, _("read of %qT"), type);
290 else
291 return fmt_styled_string (sm, _("write of %qT"), type);
294 if (op.m_dir == DIR_READ)
295 return styled_string (sm, _("read"));
296 else
297 return styled_string (sm, _("write"));
300 /* Subroutine of clean_up_for_diagram. */
302 static tree
303 strip_any_cast (tree expr)
305 if (TREE_CODE (expr) == NOP_EXPR
306 || TREE_CODE (expr) == NON_LVALUE_EXPR)
307 expr = TREE_OPERAND (expr, 0);
308 return expr;
311 /* Duplicate EXPR, replacing any SSA names with the underlying variable. */
313 tree
314 remove_ssa_names (tree expr)
316 if (TREE_CODE (expr) == SSA_NAME
317 && SSA_NAME_VAR (expr))
318 return SSA_NAME_VAR (expr);
319 tree t = copy_node (expr);
320 for (int i = 0; i < TREE_OPERAND_LENGTH (expr); i++)
321 if (TREE_OPERAND (expr, i))
322 TREE_OPERAND (t, i) = remove_ssa_names (TREE_OPERAND (expr, i));
323 return t;
326 /* We want to be able to print tree expressions from the analyzer,
327 which is in the middle end.
329 We could use the front-end pretty_printer's formatting routine,
330 but:
331 (a) some have additional state in a pretty_printer subclass, so we'd
332 need to clone global_dc->printer
333 (b) the "aka" type information added by the C and C++ frontends are
334 too verbose when building a diagram, and there isn't a good way to ask
335 for a less verbose version of them.
337 Hence we use default_tree_printer.
338 However, we want to avoid printing SSA names, and instead print the
339 underlying var name.
340 Ideally there would be a better tree printer for use by middle end
341 warnings, but as workaround, this function clones a tree, replacing
342 SSA names with the var names. */
344 tree
345 clean_up_for_diagram (tree expr)
347 tree without_ssa_names = remove_ssa_names (expr);
348 return strip_any_cast (without_ssa_names);
351 /* struct bit_size_expr. */
353 /* Attempt to generate a user-facing styled string that mentions this
354 bit_size_expr.
355 Use MODEL for extracting representative tree values where necessary.
356 The CONCRETE_* format strings should contain a single %wi.
357 The SYMBOLIC_* format strings should contain a single %qs.
358 Return nullptr if unable to represent the expression. */
360 std::unique_ptr<text_art::styled_string>
361 bit_size_expr::maybe_get_formatted_str (text_art::style_manager &sm,
362 const region_model &model,
363 const char *concrete_single_bit_fmt,
364 const char *concrete_plural_bits_fmt,
365 const char *concrete_single_byte_fmt,
366 const char *concrete_plural_bytes_fmt,
367 const char *symbolic_bits_fmt,
368 const char *symbolic_bytes_fmt) const
370 region_model_manager &mgr = *model.get_manager ();
371 if (const svalue *num_bytes = maybe_get_as_bytes (mgr))
373 if (tree cst = num_bytes->maybe_get_constant ())
375 byte_size_t concrete_num_bytes = wi::to_offset (cst);
376 if (!wi::fits_uhwi_p (concrete_num_bytes))
377 return nullptr;
378 if (concrete_num_bytes == 1)
379 return ::make_unique <text_art::styled_string>
380 (fmt_styled_string (sm, concrete_single_byte_fmt,
381 concrete_num_bytes.to_uhwi ()));
382 else
383 return ::make_unique <text_art::styled_string>
384 (fmt_styled_string (sm, concrete_plural_bytes_fmt,
385 concrete_num_bytes.to_uhwi ()));
387 else
389 pretty_printer pp;
390 pp_format_decoder (&pp) = default_tree_printer;
391 if (!num_bytes->maybe_print_for_user (&pp, model))
392 return nullptr;
393 return ::make_unique <text_art::styled_string>
394 (fmt_styled_string (sm, symbolic_bytes_fmt,
395 pp_formatted_text (&pp)));
398 else if (tree cst = m_num_bits.maybe_get_constant ())
400 bit_size_t concrete_num_bits = wi::to_offset (cst);
401 if (!wi::fits_uhwi_p (concrete_num_bits))
402 return nullptr;
403 if (concrete_num_bits == 1)
404 return ::make_unique <text_art::styled_string>
405 (fmt_styled_string (sm, concrete_single_bit_fmt,
406 concrete_num_bits.to_uhwi ()));
407 else
408 return ::make_unique <text_art::styled_string>
409 (fmt_styled_string (sm, concrete_plural_bits_fmt,
410 concrete_num_bits.to_uhwi ()));
412 else
414 pretty_printer pp;
415 pp_format_decoder (&pp) = default_tree_printer;
416 if (!m_num_bits.maybe_print_for_user (&pp, model))
417 return nullptr;
418 return ::make_unique <text_art::styled_string>
419 (fmt_styled_string (sm, symbolic_bits_fmt,
420 pp_formatted_text (&pp)));
424 bool
425 bit_size_expr::maybe_print_for_user (pretty_printer *pp,
426 const region_model &model) const
428 if (tree cst = m_num_bits.maybe_get_constant ())
430 bit_size_t concrete_num_bits = wi::to_offset (cst);
431 pp_bit_size_t (pp, concrete_num_bits);
432 return true;
434 else
436 if (const svalue *num_bytes = maybe_get_as_bytes (*model.get_manager ()))
438 pretty_printer tmp_pp;
439 pp_format_decoder (&tmp_pp) = default_tree_printer;
440 if (!num_bytes->maybe_print_for_user (&tmp_pp, model))
441 return false;
442 pp_printf (pp, _("%qs bytes"), pp_formatted_text (&tmp_pp));
443 return true;
445 else
447 pretty_printer tmp_pp;
448 pp_format_decoder (&tmp_pp) = default_tree_printer;
449 if (!m_num_bits.maybe_print_for_user (&tmp_pp, model))
450 return false;
451 pp_printf (pp, _("%qs bits"), pp_formatted_text (&tmp_pp));
452 return true;
457 /* Attempt to get a symbolic value for this symbolic bit size,
458 expressed in bytes.
459 Return null if it's not known to divide exactly. */
461 const svalue *
462 bit_size_expr::maybe_get_as_bytes (region_model_manager &mgr) const
464 if (tree cst = m_num_bits.maybe_get_constant ())
466 bit_offset_t concrete_bits = wi::to_offset (cst);
467 if (concrete_bits % BITS_PER_UNIT != 0)
468 /* Not an exact multiple, so fail. */
469 return nullptr;
471 const svalue *bits_per_byte
472 = mgr.get_or_create_int_cst (NULL_TREE, BITS_PER_UNIT);
473 return mgr.maybe_fold_binop (NULL_TREE, EXACT_DIV_EXPR,
474 &m_num_bits, bits_per_byte);
477 /* struct access_range. */
479 access_range::access_range (const region *base_region, const bit_range &bits)
480 : m_start (region_offset::make_concrete (base_region,
481 bits.get_start_bit_offset ())),
482 m_next (region_offset::make_concrete (base_region,
483 bits.get_next_bit_offset ()))
487 access_range::access_range (const region *base_region, const byte_range &bytes)
488 : m_start (region_offset::make_concrete (base_region,
489 bytes.get_start_bit_offset ())),
490 m_next (region_offset::make_concrete (base_region,
491 bytes.get_next_bit_offset ()))
495 access_range::access_range (const region &reg, region_model_manager *mgr)
496 : m_start (strip_types (reg.get_offset (mgr), *mgr)),
497 m_next (strip_types (reg.get_next_offset (mgr), *mgr))
501 bit_size_expr
502 access_range::get_size (region_model_manager *mgr) const
504 const svalue &start_bit_offset = m_start.calc_symbolic_bit_offset (mgr);
505 const svalue &next_bit_offset = m_next.calc_symbolic_bit_offset (mgr);
506 return bit_size_expr
507 (*mgr->get_or_create_binop (NULL_TREE, MINUS_EXPR,
508 &next_bit_offset, &start_bit_offset));
511 bool
512 access_range::contains_p (const access_range &other) const
514 return (m_start <= other.m_start
515 && other.m_next <= m_next);
518 bool
519 access_range::empty_p () const
521 bit_range concrete_bits (0, 0);
522 if (!as_concrete_bit_range (&concrete_bits))
523 return false;
524 return concrete_bits.empty_p ();
527 void
528 access_range::dump_to_pp (pretty_printer *pp, bool simple) const
530 if (m_start.concrete_p () && m_next.concrete_p ())
532 bit_range bits (m_start.get_bit_offset (),
533 m_next.get_bit_offset () - m_start.get_bit_offset ());
534 bits.dump_to_pp (pp);
535 return;
537 pp_character (pp, '[');
538 m_start.dump_to_pp (pp, simple);
539 pp_string (pp, " to ");
540 m_next.dump_to_pp (pp, simple);
541 pp_character (pp, ')');
544 DEBUG_FUNCTION void
545 access_range::dump (bool simple) const
547 pretty_printer pp;
548 pp_format_decoder (&pp) = default_tree_printer;
549 pp_show_color (&pp) = pp_show_color (global_dc->printer);
550 pp.set_output_stream (stderr);
551 dump_to_pp (&pp, simple);
552 pp_newline (&pp);
553 pp_flush (&pp);
556 void
557 access_range::log (const char *title, logger &logger) const
559 logger.start_log_line ();
560 logger.log_partial ("%s: ", title);
561 dump_to_pp (logger.get_printer (), true);
562 logger.end_log_line ();
565 /* struct access_operation. */
567 access_range
568 access_operation::get_valid_bits () const
570 const svalue *capacity_in_bytes_sval = m_model.get_capacity (m_base_region);
571 return access_range
572 (region_offset::make_concrete (m_base_region, 0),
573 region_offset::make_byte_offset (m_base_region, capacity_in_bytes_sval),
574 *get_manager ());
577 access_range
578 access_operation::get_actual_bits () const
580 return access_range (m_reg, get_manager ());
583 /* If there are any bits accessed invalidly before the valid range,
584 return true and write their range to *OUT.
585 Return false if there aren't, or if there's a problem
586 (e.g. symbolic ranges. */
588 bool
589 access_operation::maybe_get_invalid_before_bits (access_range *out) const
591 access_range valid_bits (get_valid_bits ());
592 access_range actual_bits (get_actual_bits ());
594 if (actual_bits.m_start >= valid_bits.m_start)
596 /* No part of accessed range is before the valid range. */
597 return false;
599 else if (actual_bits.m_next > valid_bits.m_start)
601 /* Get part of accessed range that's before the valid range. */
602 *out = access_range (actual_bits.m_start, valid_bits.m_start,
603 *get_manager ());
604 return true;
606 else
608 /* Accessed range is fully before valid range. */
609 *out = actual_bits;
610 return true;
614 /* If there are any bits accessed invalidly after the valid range,
615 return true and write their range to *OUT.
616 Return false if there aren't, or if there's a problem. */
618 bool
619 access_operation::maybe_get_invalid_after_bits (access_range *out) const
621 access_range valid_bits (get_valid_bits ());
622 access_range actual_bits (get_actual_bits ());
624 if (actual_bits.m_next <= valid_bits.m_next)
626 /* No part of accessed range is after the valid range. */
627 return false;
629 else if (actual_bits.m_start < valid_bits.m_next)
631 /* Get part of accessed range that's after the valid range. */
632 *out = access_range (valid_bits.m_next, actual_bits.m_next,
633 *get_manager ());
634 return true;
636 else
638 /* Accessed range is fully after valid range. */
639 *out = actual_bits;
640 return true;
644 /* A class for capturing all of the region offsets of interest (both concrete
645 and symbolic), to help align everything in the diagram.
646 Boundaries can be soft or hard; hard boundaries are emphasized visually
647 (e.g. the boundary between valid vs invalid accesses).
649 Offsets in the boundaries are all expressed relative to the base
650 region of the access_operation. */
652 class boundaries
654 public:
655 enum class kind { HARD, SOFT};
657 boundaries (const region &base_reg, logger *logger)
658 : m_base_reg (base_reg), m_logger (logger)
662 void add (region_offset offset, enum kind k)
664 m_all_offsets.insert (offset);
665 if (k == kind::HARD)
666 m_hard_offsets.insert (offset);
669 void add (const access_range &range, enum kind kind)
671 add (range.m_start, kind);
672 add (range.m_next, kind);
673 if (m_logger)
675 m_logger->start_log_line ();
676 m_logger->log_partial ("added access_range: ");
677 range.dump_to_pp (m_logger->get_printer (), true);
678 m_logger->log_partial (" (%s)",
679 (kind == boundaries::kind::HARD)
680 ? "HARD" : "soft");
681 m_logger->end_log_line ();
685 void add (const region &reg, region_model_manager *mgr, enum kind kind)
687 add (access_range (reg.get_offset (mgr),
688 reg.get_next_offset (mgr),
689 *mgr),
690 kind);
693 void add (const byte_range bytes, enum kind kind)
695 add (access_range (&m_base_reg, bytes), kind);
698 void add_all_bytes_in_range (const byte_range &bytes)
700 for (byte_offset_t byte_idx = bytes.get_start_byte_offset ();
701 byte_idx <= bytes.get_next_byte_offset ();
702 byte_idx = byte_idx + 1)
703 add (region_offset::make_concrete (&m_base_reg, byte_idx * 8),
704 kind::SOFT);
707 void add_all_bytes_in_range (const access_range &range)
709 byte_range bytes (0, 0);
710 bool valid = range.as_concrete_byte_range (&bytes);
711 gcc_assert (valid);
712 add_all_bytes_in_range (bytes);
715 void log (logger &logger) const
717 logger.log ("boundaries:");
718 logger.inc_indent ();
719 for (auto offset : m_all_offsets)
721 enum kind k = get_kind (offset);
722 logger.start_log_line ();
723 logger.log_partial ("%s: ", (k == kind::HARD) ? "HARD" : "soft");
724 offset.dump_to_pp (logger.get_printer (), true);
725 logger.end_log_line ();
727 logger.dec_indent ();
730 enum kind get_kind (region_offset offset) const
732 gcc_assert (m_all_offsets.find (offset) != m_all_offsets.end ());
733 if (m_hard_offsets.find (offset) != m_hard_offsets.end ())
734 return kind::HARD;
735 else
736 return kind::SOFT;
739 std::set<region_offset>::const_iterator begin () const
741 return m_all_offsets.begin ();
743 std::set<region_offset>::const_iterator end () const
745 return m_all_offsets.end ();
747 std::set<region_offset>::size_type size () const
749 return m_all_offsets.size ();
752 std::vector<region_offset>
753 get_hard_boundaries_in_range (byte_offset_t min_offset,
754 byte_offset_t max_offset) const
756 std::vector<region_offset> result;
757 for (auto &offset : m_hard_offsets)
759 if (!offset.concrete_p ())
760 continue;
761 byte_offset_t byte;
762 if (!offset.get_concrete_byte_offset (&byte))
763 continue;
764 if (byte < min_offset)
765 continue;
766 if (byte > max_offset)
767 continue;
768 result.push_back (offset);
770 return result;
773 private:
774 const region &m_base_reg;
775 logger *m_logger;
776 std::set<region_offset> m_all_offsets;
777 std::set<region_offset> m_hard_offsets;
780 /* A widget that wraps a table but offloads column-width calculation
781 to a shared object, so that we can vertically line up multiple tables
782 and have them all align their columns.
784 For example, in:
786 01| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
787 02| |[0]|[1]|[2]|[3]|[4]|[5]| ... |[440]|[441]|[442]|[443]|[444]|[445]|
788 03| +---+---+---+---+---+---+ +-----+-----+-----+-----+-----+-----+
789 04| |'L'|'o'|'r'|'e'|'m'|' '| | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
790 05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
791 06| | string literal (type: 'char[446]') |
792 07| +----------------------------------------------------------------------+
793 08| | | | | | | | | | | | | | | |
794 09| | | | | | | | | | | | | | | |
795 10| v v v v v v v v v v v v v v v
796 11|+---+---------------------+----++--------------------------------------+
797 12||[0]| ... |[99]|| after valid range |
798 13|+---+---------------------+----+| |
799 14|| 'buf' (type: 'char[100]') || |
800 15|+------------------------------++--------------------------------------+
801 16||~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
802 17| | |
803 18| +---------+---------+ +----------+----------+
804 19| |capacity: 100 bytes| |overflow of 346 bytes|
805 20| +-------------------+ +---------------------+
807 rows 01-07 and rows 11-15 are x_aligned_table_widget instances. */
809 class x_aligned_table_widget : public leaf_widget
811 public:
812 x_aligned_table_widget (table t,
813 const theme &theme,
814 table_dimension_sizes &col_widths)
815 : m_table (std::move (t)),
816 m_theme (theme),
817 m_col_widths (col_widths),
818 m_row_heights (t.get_size ().h),
819 m_cell_sizes (m_col_widths, m_row_heights),
820 m_tg (m_table, m_cell_sizes)
824 const char *get_desc () const override
826 return "x_aligned_table_widget";
829 canvas::size_t calc_req_size () final override
831 /* We don't compute the size requirements;
832 the parent should have done this. */
833 return m_tg.get_canvas_size ();
836 void paint_to_canvas (canvas &canvas) final override
838 m_table.paint_to_canvas (canvas,
839 get_top_left (),
840 m_tg,
841 m_theme);
844 const table &get_table () const { return m_table; }
845 table_cell_sizes &get_cell_sizes () { return m_cell_sizes; }
846 void recalc_coords ()
848 m_tg.recalc_coords ();
851 private:
852 table m_table;
853 const theme &m_theme;
854 table_dimension_sizes &m_col_widths; // Reference to shared column widths
855 table_dimension_sizes m_row_heights; // Unique row heights
856 table_cell_sizes m_cell_sizes;
857 table_geometry m_tg;
860 /* A widget for printing arrows between the accessed region
861 and the svalue, showing the direction of the access.
863 For example, in:
865 01| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
866 02| |[0]|[1]|[2]|[3]|[4]|[5]| ... |[440]|[441]|[442]|[443]|[444]|[445]|
867 03| +---+---+---+---+---+---+ +-----+-----+-----+-----+-----+-----+
868 04| |'L'|'o'|'r'|'e'|'m'|' '| | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
869 05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
870 06| | string literal (type: 'char[446]') |
871 07| +----------------------------------------------------------------------+
872 08| | | | | | | | | | | | | | | |
873 09| | | | | | | | | | | | | | | |
874 10| v v v v v v v v v v v v v v v
875 11|+---+---------------------+----++--------------------------------------+
876 12||[0]| ... |[99]|| after valid range |
877 13|+---+---------------------+----+| |
878 14|| 'buf' (type: 'char[100]') || |
879 15|+------------------------------++--------------------------------------+
880 16||~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
881 17| | |
882 18| +---------+---------+ +----------+----------+
883 19| |capacity: 100 bytes| |overflow of 346 bytes|
884 20| +-------------------+ +---------------------+
886 rows 8-10 are the direction widget. */
888 class direction_widget : public leaf_widget
890 public:
891 direction_widget (const access_diagram_impl &dia_impl,
892 const bit_to_table_map &btm)
893 : leaf_widget (),
894 m_dia_impl (dia_impl),
895 m_btm (btm)
898 const char *get_desc () const override
900 return "direction_widget";
902 canvas::size_t calc_req_size () final override
904 /* Get our width from our siblings. */
905 return canvas::size_t (0, 3);
907 void paint_to_canvas (canvas &canvas) final override;
909 private:
910 const access_diagram_impl &m_dia_impl;
911 const bit_to_table_map &m_btm;
914 /* A widget for adding an x_ruler to a diagram based on table columns,
915 offloading column-width calculation to shared objects, so that the ruler
916 lines up with other tables in the diagram.
918 For example, in:
920 01| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
921 02| |[0]|[1]|[2]|[3]|[4]|[5]| ... |[440]|[441]|[442]|[443]|[444]|[445]|
922 03| +---+---+---+---+---+---+ +-----+-----+-----+-----+-----+-----+
923 04| |'L'|'o'|'r'|'e'|'m'|' '| | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
924 05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
925 06| | string literal (type: 'char[446]') |
926 07| +----------------------------------------------------------------------+
927 08| | | | | | | | | | | | | | | |
928 09| | | | | | | | | | | | | | | |
929 10| v v v v v v v v v v v v v v v
930 11|+---+---------------------+----++--------------------------------------+
931 12||[0]| ... |[99]|| after valid range |
932 13|+---+---------------------+----+| |
933 14|| 'buf' (type: 'char[100]') || |
934 15|+------------------------------++--------------------------------------+
935 16||~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
936 17| | |
937 18| +---------+---------+ +----------+----------+
938 19| |capacity: 100 bytes| |overflow of 346 bytes|
939 20| +-------------------+ +---------------------+
941 rows 16-20 are the x_aligned_x_ruler_widget. */
943 class x_aligned_x_ruler_widget : public leaf_widget
945 public:
946 x_aligned_x_ruler_widget (const access_diagram_impl &dia_impl,
947 const theme &theme)
948 : m_dia_impl (dia_impl),
949 m_theme (theme)
953 const char *get_desc () const override
955 return "x_aligned_ruler_widget";
958 void add_range (const table::range_t &x_range,
959 styled_string text,
960 style::id_t style_id)
962 m_labels.push_back (label (x_range, std::move (text), style_id));
965 canvas::size_t calc_req_size () final override
967 x_ruler r (make_x_ruler ());
968 return r.get_size ();
971 void paint_to_canvas (canvas &canvas) final override
973 x_ruler r (make_x_ruler ());
974 r.paint_to_canvas (canvas,
975 get_top_left (),
976 m_theme);
979 private:
980 struct label
982 label (const table::range_t &table_x_range,
983 styled_string text,
984 style::id_t style_id)
985 : m_table_x_range (table_x_range),
986 m_text (std::move (text)),
987 m_style_id (style_id)
990 table::range_t m_table_x_range;
991 styled_string m_text;
992 style::id_t m_style_id;
995 x_ruler make_x_ruler () const;
997 const access_diagram_impl &m_dia_impl;
998 const theme &m_theme;
999 std::vector<label> m_labels;
1002 /* A two-way mapping between access_ranges and table columns, for use by
1003 spatial_item subclasses for creating tables.
1004 For example when visualizing a bogus access of 'int arr[10];'
1005 at 'arr[10]', we might have:
1006 - table column 0 is "bytes 0-3" (for arr[0])
1007 - table column 1 is "bytes 4-35" (for arr[1] through arr[8])
1008 - table column 2 is "bytes 36-39 (for arr[9])
1009 - table column 3 is blank to emphasize a hard boundary between
1010 valid/invalid accesses.
1011 - table column 4 is "bytes 40-44" (for arr[10])
1013 We store this as a pair of maps from region_offset to table x; in
1014 the abvove example:
1016 region offset table_x prev_table_x
1017 bit 0 (aka byte 0) 0 (none)
1018 bit 32 (aka byte 4) 1 0
1019 bit 288 (aka byte 36) 2 1
1020 bit 320 (aka byte 40) 4 2
1021 bit 352 (aka byte 44) (none) (none)
1023 so that e.g given the half-open byte range [0, 40)
1024 we can determine the closed range of table x [0, 2]. */
1026 class bit_to_table_map
1028 public:
1029 /* Populate m_table_x_for_bit and m_bit_for_table_x. */
1030 void populate (const boundaries &boundaries,
1031 region_model_manager &mgr,
1032 logger *logger)
1034 LOG_SCOPE (logger);
1036 int table_x = 0;
1037 std::vector <region_offset> vec_boundaries (boundaries.begin (),
1038 boundaries.end ());
1040 /* Sort into an order that makes sense. */
1041 std::sort (vec_boundaries.begin (),
1042 vec_boundaries.end ());
1044 if (logger)
1046 logger->log ("vec_boundaries");
1047 logger->inc_indent ();
1048 for (unsigned idx = 0; idx < vec_boundaries.size (); idx++)
1050 logger->start_log_line ();
1051 logger->log_partial ("idx: %i: ", idx);
1052 vec_boundaries[idx].dump_to_pp (logger->get_printer (), true);
1053 logger->end_log_line ();
1055 logger->dec_indent ();
1058 for (size_t idx = 0; idx < vec_boundaries.size (); idx++)
1060 const region_offset &offset = vec_boundaries[idx];
1061 if (idx > 0 && (idx + 1) < vec_boundaries.size ())
1063 if (boundaries.get_kind (offset) == boundaries::kind::HARD)
1064 table_x += 1;
1066 m_table_x_for_offset[offset] = table_x;
1067 if ((idx + 1) < vec_boundaries.size ())
1069 const region_offset &next_offset = vec_boundaries[idx + 1];
1070 m_table_x_for_prev_offset[next_offset] = table_x;
1071 m_range_for_table_x[table_x]
1072 = access_range (offset, next_offset, mgr);
1074 table_x += 1;
1076 m_num_columns = table_x - 1;
1078 if (logger)
1079 log (*logger);
1082 unsigned get_num_columns () const
1084 return m_num_columns;
1087 table::range_t get_table_x_for_range (const access_range &range) const
1089 return table::range_t (get_table_x_for_offset (range.m_start),
1090 get_table_x_for_prev_offset (range.m_next) + 1);
1093 table::rect_t get_table_rect (const access_range &range,
1094 const int table_y, const int table_h) const
1096 const table::range_t x_range (get_table_x_for_range (range));
1097 return table::rect_t (table::coord_t (x_range.start, table_y),
1098 table::size_t (x_range.get_size (), table_h));
1101 table::rect_t get_table_rect (const region *base_reg,
1102 const bit_range &bits,
1103 const int table_y, const int table_h) const
1105 const access_range range (base_reg, bits);
1106 return get_table_rect (range, table_y, table_h);
1109 table::rect_t get_table_rect (const region *base_reg,
1110 const byte_range &bytes,
1111 const int table_y, const int table_h) const
1113 return get_table_rect (base_reg, bytes.as_bit_range (), table_y, table_h);
1116 bool maybe_get_access_range_for_table_x (int table_x,
1117 access_range *out) const
1119 auto slot = m_range_for_table_x.find (table_x);
1120 if (slot == m_range_for_table_x.end ())
1121 return false;
1122 *out = slot->second;
1123 return true;
1126 void log (logger &logger) const
1128 logger.log ("table columns");
1129 logger.inc_indent ();
1130 for (unsigned table_x = 0; table_x < get_num_columns (); table_x++)
1132 logger.start_log_line ();
1133 logger.log_partial ("table_x: %i", table_x);
1134 access_range range_for_column (NULL, bit_range (0, 0));
1135 if (maybe_get_access_range_for_table_x (table_x, &range_for_column))
1137 logger.log_partial (": range: ");
1138 range_for_column.dump_to_pp (logger.get_printer (), true);
1140 logger.end_log_line ();
1142 logger.dec_indent ();
1145 int get_table_x_for_offset (region_offset offset) const
1147 auto slot = m_table_x_for_offset.find (offset);
1149 /* If this fails, then we probably failed to fully populate m_boundaries
1150 in find_boundaries. */
1151 gcc_assert (slot != m_table_x_for_offset.end ());
1153 return slot->second;
1156 private:
1157 int get_table_x_for_prev_offset (region_offset offset) const
1159 auto slot = m_table_x_for_prev_offset.find (offset);
1161 /* If this fails, then we probably failed to fully populate m_boundaries
1162 in find_boundaries. */
1163 gcc_assert (slot != m_table_x_for_prev_offset.end ());
1165 return slot->second;
1168 std::map<region_offset, int> m_table_x_for_offset;
1169 std::map<region_offset, int> m_table_x_for_prev_offset;
1170 std::map<int, access_range> m_range_for_table_x;
1171 unsigned m_num_columns;
1174 /* Base class for something in the diagram that participates
1175 in two steps of diagram creation:
1176 (a) populating a boundaries instance with the boundaries of interest
1177 (b) creating a table instance for itself.
1179 Offsets in the boundaries are all expressed relative to the base
1180 region of the access_operation. */
1182 class spatial_item
1184 public:
1185 virtual ~spatial_item () {}
1186 virtual void add_boundaries (boundaries &out, logger *) const = 0;
1188 virtual table make_table (const bit_to_table_map &btm,
1189 style_manager &sm) const = 0;
1192 /* A spatial_item that involves showing an svalue at a particular offset. */
1194 class svalue_spatial_item : public spatial_item
1196 public:
1197 enum class kind
1199 WRITTEN,
1200 EXISTING
1202 protected:
1203 svalue_spatial_item (const svalue &sval,
1204 access_range bits,
1205 enum kind kind)
1206 : m_sval (sval), m_bits (bits), m_kind (kind)
1210 const svalue &m_sval;
1211 access_range m_bits;
1212 enum kind m_kind;
1215 static std::unique_ptr<spatial_item>
1216 make_existing_svalue_spatial_item (const svalue *sval,
1217 const access_range &bits,
1218 const theme &theme);
1220 class compound_svalue_spatial_item : public svalue_spatial_item
1222 public:
1223 compound_svalue_spatial_item (const compound_svalue &sval,
1224 const access_range &bits,
1225 enum kind kind,
1226 const theme &theme)
1227 : svalue_spatial_item (sval, bits, kind),
1228 m_compound_sval (sval)
1230 const binding_map &map = m_compound_sval.get_map ();
1231 auto_vec <const binding_key *> binding_keys;
1232 for (auto iter : map)
1234 const binding_key *key = iter.first;
1235 const svalue *bound_sval = iter.second;
1236 if (const concrete_binding *concrete_key
1237 = key->dyn_cast_concrete_binding ())
1239 access_range range (nullptr,
1240 concrete_key->get_bit_range ());
1241 if (std::unique_ptr<spatial_item> child
1242 = make_existing_svalue_spatial_item (bound_sval,
1243 range,
1244 theme))
1245 m_children.push_back (std::move (child));
1250 void add_boundaries (boundaries &out, logger *logger) const final override
1252 LOG_SCOPE (logger);
1253 for (auto &iter : m_children)
1254 iter->add_boundaries (out, logger);
1257 table make_table (const bit_to_table_map &btm,
1258 style_manager &sm) const final override
1260 std::vector<table> child_tables;
1261 int max_rows = 0;
1262 for (auto &iter : m_children)
1264 table child_table (iter->make_table (btm, sm));
1265 max_rows = MAX (max_rows, child_table.get_size ().h);
1266 child_tables.push_back (std::move (child_table));
1268 table t (table::size_t (btm.get_num_columns (), max_rows));
1269 for (auto &&child_table : child_tables)
1270 t.add_other_table (std::move (child_table),
1271 table::coord_t (0, 0));
1272 return t;
1275 private:
1276 const compound_svalue &m_compound_sval;
1277 std::vector<std::unique_ptr<spatial_item>> m_children;
1280 /* Loop through the TABLE_X_RANGE columns of T, adding
1281 cells containing "..." in any unoccupied ranges of table cell. */
1283 static void
1284 add_ellipsis_to_gaps (table &t,
1285 style_manager &sm,
1286 const table::range_t &table_x_range,
1287 const table::range_t &table_y_range)
1289 int table_x = table_x_range.get_min ();
1290 while (table_x < table_x_range.get_next ())
1292 /* Find a run of unoccupied table cells. */
1293 const int start_table_x = table_x;
1294 while (table_x < table_x_range.get_next ()
1295 && !t.get_placement_at (table::coord_t (table_x,
1296 table_y_range.get_min ())))
1297 table_x++;
1298 const table::range_t unoccupied_x_range (start_table_x, table_x);
1299 if (unoccupied_x_range.get_size () > 0)
1300 t.set_cell_span (table::rect_t (unoccupied_x_range, table_y_range),
1301 styled_string (sm, "..."));
1302 /* Skip occupied table cells. */
1303 while (table_x < table_x_range.get_next ()
1304 && t.get_placement_at (table::coord_t (table_x,
1305 table_y_range.get_min ())))
1306 table_x++;
1310 /* Subclass of spatial_item for visualizing the region of memory
1311 that's valid to access relative to the base region of region accessed in
1312 the operation. */
1314 class valid_region_spatial_item : public spatial_item
1316 public:
1317 valid_region_spatial_item (const access_operation &op,
1318 diagnostic_event_id_t region_creation_event_id,
1319 const theme &theme)
1320 : m_op (op),
1321 m_region_creation_event_id (region_creation_event_id),
1322 m_boundaries (nullptr),
1323 m_existing_sval (op.m_model.get_store_value (op.m_base_region, nullptr)),
1324 m_existing_sval_spatial_item
1325 (make_existing_svalue_spatial_item (m_existing_sval,
1326 op.get_valid_bits (),
1327 theme))
1331 void add_boundaries (boundaries &out, logger *logger) const final override
1333 LOG_SCOPE (logger);
1334 m_boundaries = &out;
1335 access_range valid_bits = m_op.get_valid_bits ();
1336 if (logger)
1338 logger->start_log_line ();
1339 logger->log_partial ("valid bits: ");
1340 valid_bits.dump_to_pp (logger->get_printer (), true);
1341 logger->end_log_line ();
1343 out.add (valid_bits, boundaries::kind::HARD);
1345 if (m_existing_sval_spatial_item)
1347 if (logger)
1349 logger->start_log_line ();
1350 logger->log_partial ("existing svalue: ");
1351 m_existing_sval->dump_to_pp (logger->get_printer (), true);
1352 logger->end_log_line ();
1354 m_existing_sval_spatial_item->add_boundaries (out, logger);
1357 /* Support for showing first and final element in array types. */
1358 if (tree base_type = m_op.m_base_region->get_type ())
1359 if (TREE_CODE (base_type) == ARRAY_TYPE)
1361 if (logger)
1362 logger->log ("showing first and final element in array type");
1363 region_model_manager *mgr = m_op.m_model.get_manager ();
1364 tree domain = TYPE_DOMAIN (base_type);
1365 if (domain && TYPE_MIN_VALUE (domain) && TYPE_MAX_VALUE (domain))
1367 const svalue *min_idx_sval
1368 = mgr->get_or_create_constant_svalue (TYPE_MIN_VALUE (domain));
1369 const svalue *max_idx_sval
1370 = mgr->get_or_create_constant_svalue (TYPE_MAX_VALUE (domain));
1371 const region *min_element =
1372 mgr->get_element_region (m_op.m_base_region,
1373 TREE_TYPE (base_type),
1374 min_idx_sval);
1375 out.add (*min_element, mgr, boundaries::kind::SOFT);
1376 const region *max_element =
1377 mgr->get_element_region (m_op.m_base_region,
1378 TREE_TYPE (base_type),
1379 max_idx_sval);
1380 out.add (*max_element, mgr, boundaries::kind::SOFT);
1385 /* Subroutine of make_table when base region has ARRAY_TYPE. */
1386 void add_array_elements_to_table (table &t,
1387 const bit_to_table_map &btm,
1388 style_manager &sm) const
1390 tree base_type = m_op.m_base_region->get_type ();
1391 gcc_assert (TREE_CODE (base_type) == ARRAY_TYPE);
1392 gcc_assert (m_boundaries != nullptr);
1394 tree domain = TYPE_DOMAIN (base_type);
1395 if (!(domain && TYPE_MIN_VALUE (domain) && TYPE_MAX_VALUE (domain)))
1396 return;
1398 const int table_y = 0;
1399 const int table_h = 1;
1400 const table::range_t table_y_range (table_y, table_y + table_h);
1402 t.add_row ();
1404 const table::range_t min_x_range
1405 = maybe_add_array_index_to_table (t, btm, sm, table_y_range,
1406 TYPE_MIN_VALUE (domain));
1407 const table::range_t max_x_range
1408 = maybe_add_array_index_to_table (t, btm, sm, table_y_range,
1409 TYPE_MAX_VALUE (domain));
1411 if (TREE_TYPE (base_type) == char_type_node)
1413 /* For a char array,: if there are any hard boundaries in
1414 m_boundaries that are *within* the valid region,
1415 then show those index values. */
1416 std::vector<region_offset> hard_boundaries
1417 = m_boundaries->get_hard_boundaries_in_range
1418 (tree_to_shwi (TYPE_MIN_VALUE (domain)),
1419 tree_to_shwi (TYPE_MAX_VALUE (domain)));
1420 for (auto &offset : hard_boundaries)
1422 const int table_x = btm.get_table_x_for_offset (offset);
1423 if (!offset.concrete_p ())
1424 continue;
1425 byte_offset_t byte;
1426 if (!offset.get_concrete_byte_offset (&byte))
1427 continue;
1428 table::range_t table_x_range (table_x, table_x + 1);
1429 t.maybe_set_cell_span (table::rect_t (table_x_range,
1430 table_y_range),
1431 fmt_styled_string (sm, "[%wi]",
1432 byte.to_shwi ()));
1436 add_ellipsis_to_gaps (t, sm,
1437 table::range_t (min_x_range.get_next (),
1438 max_x_range.get_min ()),
1439 table_y_range);
1442 table::range_t
1443 maybe_add_array_index_to_table (table &t,
1444 const bit_to_table_map &btm,
1445 style_manager &sm,
1446 const table::range_t table_y_range,
1447 tree idx_cst) const
1449 region_model_manager * const mgr = m_op.get_manager ();
1450 tree base_type = m_op.m_base_region->get_type ();
1451 const svalue *idx_sval
1452 = mgr->get_or_create_constant_svalue (idx_cst);
1453 const region *element_reg = mgr->get_element_region (m_op.m_base_region,
1454 TREE_TYPE (base_type),
1455 idx_sval);
1456 const access_range element_range (*element_reg, mgr);
1457 const table::range_t element_x_range
1458 = btm.get_table_x_for_range (element_range);
1460 t.maybe_set_cell_span (table::rect_t (element_x_range,
1461 table_y_range),
1462 fmt_styled_string (sm, "[%E]", idx_cst));
1464 return element_x_range;
1467 table make_table (const bit_to_table_map &btm,
1468 style_manager &sm) const final override
1470 table t (table::size_t (btm.get_num_columns (), 0));
1472 if (tree base_type = m_op.m_base_region->get_type ())
1473 if (TREE_CODE (base_type) == ARRAY_TYPE)
1474 add_array_elements_to_table (t, btm, sm);
1476 /* Make use of m_existing_sval_spatial_item, if any. */
1477 if (m_existing_sval_spatial_item)
1479 table table_for_existing
1480 = m_existing_sval_spatial_item->make_table (btm, sm);
1481 const int table_y = t.add_rows (table_for_existing.get_size ().h);
1482 t.add_other_table (std::move (table_for_existing),
1483 table::coord_t (0, table_y));
1486 access_range valid_bits = m_op.get_valid_bits ();
1487 const int table_y = t.add_row ();
1488 const int table_h = 1;
1489 table::rect_t rect = btm.get_table_rect (valid_bits, table_y, table_h);
1490 styled_string s;
1491 switch (m_op.m_base_region->get_kind ())
1493 default:
1494 s = styled_string (sm, _("region"));
1495 break;
1496 case RK_DECL:
1498 const decl_region *decl_reg
1499 = as_a <const decl_region *> (m_op.m_base_region);
1500 tree decl = decl_reg->get_decl ();
1501 s = fmt_styled_string (sm, "%qE (type: %qT)",
1502 decl,
1503 TREE_TYPE (decl));
1505 break;
1506 case RK_HEAP_ALLOCATED:
1508 if (m_region_creation_event_id.known_p ())
1509 s = fmt_styled_string (sm, _("buffer allocated on heap at %@"),
1510 &m_region_creation_event_id);
1511 else
1512 s = styled_string (sm, _("heap-allocated buffer"));
1514 break;
1515 case RK_ALLOCA:
1517 if (m_region_creation_event_id.known_p ())
1518 s = fmt_styled_string (sm, _("buffer allocated on stack at %@"),
1519 &m_region_creation_event_id);
1520 else
1521 s = styled_string (sm, _("stack-allocated buffer"));
1523 break;
1524 case RK_STRING:
1526 const string_region *string_reg
1527 = as_a <const string_region *> (m_op.m_base_region);
1528 tree string_cst = string_reg->get_string_cst ();
1529 s = fmt_styled_string (sm, _("string literal (type: %qT)"),
1530 TREE_TYPE (string_cst));
1532 break;
1534 t.set_cell_span (rect, std::move (s));
1536 return t;
1539 private:
1540 const access_operation &m_op;
1541 diagnostic_event_id_t m_region_creation_event_id;
1542 mutable const boundaries *m_boundaries;
1543 const svalue *m_existing_sval;
1544 std::unique_ptr<spatial_item> m_existing_sval_spatial_item;
1547 /* Subclass of spatial_item for visualizing the region of memory
1548 that's actually accessed by the read or write, for reads and
1549 for write cases where we don't know the svalue written. */
1551 class accessed_region_spatial_item : public spatial_item
1553 public:
1554 accessed_region_spatial_item (const access_operation &op) : m_op (op) {}
1556 void add_boundaries (boundaries &out, logger *logger) const final override
1558 LOG_SCOPE (logger);
1559 access_range actual_bits = m_op.get_actual_bits ();
1560 if (logger)
1562 logger->start_log_line ();
1563 logger->log_partial ("actual bits: ");
1564 actual_bits.dump_to_pp (logger->get_printer (), true);
1565 logger->end_log_line ();
1567 out.add (actual_bits, boundaries::kind::HARD);
1570 table make_table (const bit_to_table_map &btm,
1571 style_manager &sm) const final override
1573 table t (table::size_t (btm.get_num_columns (), 1));
1575 access_range actual_bits = m_op.get_actual_bits ();
1576 const int table_y = 0;
1577 const int table_h = 1;
1578 table::rect_t rect = btm.get_table_rect (actual_bits, table_y, table_h);
1579 t.set_cell_span (rect, styled_string (get_label_string (sm)));
1581 return t;
1584 private:
1585 styled_string get_label_string (style_manager &sm) const
1587 const access_range accessed_bits (m_op.get_actual_bits ());
1588 return get_access_size_str (sm,
1589 m_op,
1590 accessed_bits,
1591 m_op.m_reg.get_type ());
1594 const access_operation &m_op;
1597 /* Subclass of spatial_item for when we know the svalue being written
1598 to the accessed region.
1599 Can be subclassed to give visualizations of specific kinds of svalue. */
1601 class written_svalue_spatial_item : public spatial_item
1603 public:
1604 written_svalue_spatial_item (const access_operation &op,
1605 const svalue &sval,
1606 access_range actual_bits)
1607 : m_op (op), m_sval (sval), m_actual_bits (actual_bits)
1610 void add_boundaries (boundaries &out, logger *logger) const override
1612 LOG_SCOPE (logger);
1613 out.add (m_actual_bits, boundaries::kind::HARD);
1616 table make_table (const bit_to_table_map &btm,
1617 style_manager &sm) const override
1619 table t (table::size_t (btm.get_num_columns (), 0));
1621 const int table_y = t.add_row ();
1622 const int table_h = 1;
1623 table::rect_t rect = btm.get_table_rect (m_actual_bits, table_y, table_h);
1624 t.set_cell_span (rect, styled_string (get_label_string (sm)));
1625 return t;
1628 protected:
1629 styled_string get_label_string (style_manager &sm) const
1631 tree rep_tree = m_op.m_model.get_representative_tree (&m_sval);
1632 if (rep_tree)
1634 if (TREE_CODE (rep_tree) == SSA_NAME)
1635 if (tree var = SSA_NAME_VAR (rep_tree))
1636 rep_tree = var;
1637 switch (TREE_CODE (rep_tree))
1639 default:
1640 break;
1641 case INTEGER_CST:
1642 return fmt_styled_string (sm, _("write of %<(%T) %E%>"),
1643 TREE_TYPE (rep_tree),
1644 rep_tree);
1646 case PARM_DECL:
1647 case VAR_DECL:
1648 return fmt_styled_string (sm, _("write from %qE (type: %qT)"),
1649 rep_tree,
1650 TREE_TYPE (rep_tree));
1651 break;
1655 const access_range accessed_bits (m_op.get_actual_bits ());
1656 return get_access_size_str (sm,
1657 m_op,
1658 accessed_bits,
1659 m_sval.get_type ());
1662 const access_operation &m_op;
1663 const svalue &m_sval;
1664 access_range m_actual_bits;
1667 /* Subclass of svalue_spatial_item for initial_svalue of a string_region
1668 i.e. for string literals.
1670 There are three cases:
1671 (a) for long strings, show just the head and tail of the string,
1672 with an ellipsis:
1673 +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
1674 |[0]|[1]|[2]|[3]|[4]|[5]| |[440]|[441]|[442]|[443]|[444]|[445]|
1675 +---+---+---+---+---+---+ ... +-----+-----+-----+-----+-----+-----+
1676 |‘L’|‘o’|‘r’|‘e’|‘m’|‘ ’| | ‘o’ | ‘r’ | ‘u’ | ‘m’ | ‘.’ | NUL |
1677 +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
1678 | string literal (type: ‘char[446]’) |
1679 +----------------------------------------------------------------------+
1680 (b) For sufficiently short strings, show the full string:
1681 +----------+---------+---------+---------+---------+ +-----------------+
1682 | [0] | [1] | [2] | [3] | [4] | | [5] |
1683 +----------+---------+---------+---------+---------+ +-----------------+
1684 | ‘h’ | ‘e’ | ‘l’ | ‘l’ | ‘o’ | | NUL |
1685 +----------+---------+---------+---------+---------+-+-----------------+
1686 | string literal (type: ‘char[6]’) |
1687 +----------------------------------------------------------------------+
1688 (c) for non-ASCII strings that are short enough to show the full string,
1689 show how unicode code points of the bytes decoded as UTF-8:
1690 +-----+-----+-----+----+----++----+----+----+----+----+----+----+------+
1691 | [0] | [1] | [2] |[3] |[4] ||[5] |[6] |[7] |[8] |[9] |[10]|[11]| [12] |
1692 +-----+-----+-----+----+----++----+----+----+----+----+----+----+------+
1693 |0xe6 |0x96 |0x87 |0xe5|0xad||0x97|0xe5|0x8c|0x96|0xe3|0x81|0x91| 0x00 |
1694 +-----+-----+-----+----+----++----+----+----+----+----+----+----+------+
1695 | U+6587 | U+5b57 | U+5316 | U+3051 |U+0000|
1696 +-----------------+---------------+--------------+--------------+------+
1697 | string literal (type: ‘char[13]’) |
1698 +----------------------------------------------------------------------+
1699 and show the characters themselves if unicode is supported and they are not
1700 control characters:
1701 ┌─────┬─────┬─────┬────┬────┐┌────┬────┬────┬────┬────┬────┬────┬──────┐
1702 │ [0] │ [1] │ [2] │[3] │[4] ││[5] │[6] │[7] │[8] │[9] │[10]│[11]│ [12] │
1703 ├─────┼─────┼─────┼────┼────┤├────┼────┼────┼────┼────┼────┼────┼──────┤
1704 │0xe6 │0x96 │0x87 │0xe5│0xad││0x97│0xe5│0x8c│0x96│0xe3│0x81│0x91│ 0x00 │
1705 ├─────┴─────┴─────┼────┴────┴┴────┼────┴────┴────┼────┴────┴────┼──────┤
1706 │ U+6587 │ U+5b57 │ U+5316 │ U+3051 │U+0000│
1707 ├─────────────────┼───────────────┼──────────────┼──────────────┼──────┤
1708 │ 文 │ 字 │ 化 │ け │ NUL │
1709 ├─────────────────┴───────────────┴──────────────┴──────────────┴──────┤
1710 │ string literal (type: ‘char[13]’) │
1711 └──────────────────────────────────────────────────────────────────────┘
1714 class string_literal_spatial_item : public svalue_spatial_item
1716 public:
1717 string_literal_spatial_item (const svalue &sval,
1718 access_range actual_bits,
1719 const string_region &string_reg,
1720 const theme &theme,
1721 enum kind kind)
1722 : svalue_spatial_item (sval, actual_bits, kind),
1723 m_string_reg (string_reg),
1724 m_theme (theme),
1725 m_ellipsis_threshold (param_analyzer_text_art_string_ellipsis_threshold),
1726 m_ellipsis_head_len (param_analyzer_text_art_string_ellipsis_head_len),
1727 m_ellipsis_tail_len (param_analyzer_text_art_string_ellipsis_tail_len),
1728 m_show_full_string (calc_show_full_string ()),
1729 m_show_utf8 (m_show_full_string && !pure_ascii_p ())
1733 void add_boundaries (boundaries &out, logger *logger) const override
1735 LOG_SCOPE (logger);
1736 out.add (m_bits, m_kind == svalue_spatial_item::kind::WRITTEN
1737 ? boundaries::kind::HARD
1738 : boundaries::kind::SOFT);
1740 tree string_cst = get_string_cst ();
1741 /* TREE_STRING_LENGTH is sizeof, not strlen. */
1742 if (m_show_full_string)
1743 out.add_all_bytes_in_range (m_bits);
1744 else
1746 byte_range bytes (0, 0);
1747 bool valid = m_bits.as_concrete_byte_range (&bytes);
1748 gcc_assert (valid);
1749 byte_range head_of_string (bytes.get_start_byte_offset (),
1750 m_ellipsis_head_len);
1751 out.add_all_bytes_in_range (head_of_string);
1752 byte_range tail_of_string
1753 ((bytes.get_start_byte_offset ()
1754 + TREE_STRING_LENGTH (string_cst)
1755 - m_ellipsis_tail_len),
1756 m_ellipsis_tail_len);
1757 out.add_all_bytes_in_range (tail_of_string);
1758 /* Adding the above pair of ranges will also effectively add
1759 the boundaries of the range of ellipsized chars, as they're
1760 exactly in between head_of_string and tail_of_string. */
1764 table make_table (const bit_to_table_map &btm,
1765 style_manager &sm) const override
1767 table t (table::size_t (btm.get_num_columns (), 0));
1769 const int byte_idx_table_y = (m_kind == svalue_spatial_item::kind::WRITTEN
1770 ? t.add_row ()
1771 : -1);
1772 const int byte_val_table_y = t.add_row ();
1774 byte_range bytes (0, 0);
1775 bool valid = m_bits.as_concrete_byte_range (&bytes);
1776 gcc_assert (valid);
1777 tree string_cst = get_string_cst ();
1778 if (m_show_full_string)
1780 for (byte_offset_t byte_idx_within_cluster
1781 = bytes.get_start_byte_offset ();
1782 byte_idx_within_cluster < bytes.get_next_byte_offset ();
1783 byte_idx_within_cluster = byte_idx_within_cluster + 1)
1784 add_column_for_byte
1785 (t, btm, sm, byte_idx_within_cluster,
1786 byte_idx_within_cluster - bytes.get_start_byte_offset (),
1787 byte_idx_table_y, byte_val_table_y);
1789 if (m_show_utf8)
1791 const bool show_unichars = m_theme.unicode_p ();
1792 const int utf8_code_point_table_y = t.add_row ();
1793 int utf8_character_table_y;
1794 if (show_unichars)
1795 utf8_character_table_y = t.add_row ();
1797 /* We don't actually want the display widths here, but
1798 it's an easy way to decode UTF-8. */
1799 cpp_char_column_policy policy (8, cpp_wcwidth);
1800 cpp_display_width_computation dw (TREE_STRING_POINTER (string_cst),
1801 TREE_STRING_LENGTH (string_cst),
1802 policy);
1803 while (!dw.done ())
1805 cpp_decoded_char decoded_char;
1806 dw.process_next_codepoint (&decoded_char);
1808 if (!decoded_char.m_valid_ch)
1809 continue;
1810 size_t start_byte_idx
1811 = decoded_char.m_start_byte - TREE_STRING_POINTER (string_cst);
1812 byte_size_t size_in_bytes
1813 = decoded_char.m_next_byte - decoded_char.m_start_byte;
1814 byte_range cluster_bytes_for_codepoint
1815 (start_byte_idx + bytes.get_start_byte_offset (),
1816 size_in_bytes);
1818 const table::rect_t code_point_table_rect
1819 = btm.get_table_rect (&m_string_reg,
1820 cluster_bytes_for_codepoint,
1821 utf8_code_point_table_y, 1);
1822 char buf[100];
1823 sprintf (buf, "U+%04x", decoded_char.m_ch);
1824 t.set_cell_span (code_point_table_rect,
1825 styled_string (sm, buf));
1827 if (show_unichars)
1829 const table::rect_t character_table_rect
1830 = btm.get_table_rect (&m_string_reg,
1831 cluster_bytes_for_codepoint,
1832 utf8_character_table_y, 1);
1833 if (cpp_is_printable_char (decoded_char.m_ch))
1834 t.set_cell_span (character_table_rect,
1835 styled_string (decoded_char.m_ch));
1836 else if (decoded_char.m_ch == 0)
1837 t.set_cell_span (character_table_rect,
1838 styled_string (sm, "NUL"));
1839 else
1840 t.set_cell_span (character_table_rect,
1841 styled_string (sm, ""));
1846 else
1848 /* Head of string. */
1849 for (int byte_idx = 0; byte_idx < m_ellipsis_head_len; byte_idx++)
1850 add_column_for_byte (t, btm, sm,
1851 byte_idx + bytes.get_start_byte_offset (),
1852 byte_idx,
1853 byte_idx_table_y, byte_val_table_y);
1855 /* Ellipsis. */
1856 const byte_range ellipsis_bytes
1857 (m_ellipsis_head_len + bytes.get_start_byte_offset (),
1858 TREE_STRING_LENGTH (string_cst)
1859 - (m_ellipsis_head_len + m_ellipsis_tail_len));
1860 const table::rect_t table_rect
1861 = ((byte_idx_table_y != -1)
1862 ? btm.get_table_rect (&m_string_reg, ellipsis_bytes,
1863 byte_idx_table_y, 2)
1864 : btm.get_table_rect (&m_string_reg, ellipsis_bytes,
1865 byte_val_table_y, 1));
1866 t.set_cell_span(table_rect, styled_string (sm, "..."));
1868 /* Tail of string. */
1869 for (int byte_idx
1870 = (TREE_STRING_LENGTH (string_cst) - m_ellipsis_tail_len);
1871 byte_idx < TREE_STRING_LENGTH (string_cst);
1872 byte_idx++)
1873 add_column_for_byte (t, btm, sm,
1874 byte_idx + bytes.get_start_byte_offset (),
1875 byte_idx,
1876 byte_idx_table_y, byte_val_table_y);
1879 if (m_kind == svalue_spatial_item::kind::WRITTEN)
1881 const int summary_table_y = t.add_row ();
1882 t.set_cell_span (btm.get_table_rect (&m_string_reg, bytes,
1883 summary_table_y, 1),
1884 fmt_styled_string (sm,
1885 _("string literal (type: %qT)"),
1886 TREE_TYPE (string_cst)));
1889 return t;
1892 tree get_string_cst () const { return m_string_reg.get_string_cst (); }
1894 private:
1895 bool calc_show_full_string () const
1897 tree string_cst = get_string_cst ();
1898 if (TREE_STRING_LENGTH (string_cst) < m_ellipsis_threshold)
1899 return true;
1900 if (TREE_STRING_LENGTH (string_cst) <
1901 (m_ellipsis_head_len + m_ellipsis_tail_len))
1902 return true;
1903 return false;
1906 bool pure_ascii_p () const
1908 tree string_cst = get_string_cst ();
1909 for (unsigned byte_idx = 0;
1910 byte_idx < (unsigned) TREE_STRING_LENGTH (string_cst);
1911 byte_idx++)
1913 unsigned char ch = TREE_STRING_POINTER (string_cst)[byte_idx];
1914 if (ch >= 0x80)
1915 return false;
1917 return true;
1920 void add_column_for_byte (table &t, const bit_to_table_map &btm,
1921 style_manager &sm,
1922 const byte_offset_t byte_idx_within_cluster,
1923 const byte_offset_t byte_idx_within_string,
1924 const int byte_idx_table_y,
1925 const int byte_val_table_y) const
1927 tree string_cst = get_string_cst ();
1928 gcc_assert (byte_idx_within_string >= 0);
1929 gcc_assert (byte_idx_within_string < TREE_STRING_LENGTH (string_cst));
1931 const byte_range bytes (byte_idx_within_cluster, 1);
1932 if (byte_idx_table_y != -1)
1934 const table::rect_t idx_table_rect
1935 = btm.get_table_rect (&m_string_reg, bytes, byte_idx_table_y, 1);
1936 t.set_cell_span (idx_table_rect,
1937 fmt_styled_string (sm, "[%wu]",
1938 byte_idx_within_string.ulow ()));
1941 char byte_val
1942 = TREE_STRING_POINTER (string_cst)[byte_idx_within_string.ulow ()];
1943 const table::rect_t val_table_rect
1944 = btm.get_table_rect (&m_string_reg, bytes, byte_val_table_y, 1);
1945 table_cell_content content (make_cell_content_for_byte (sm, byte_val));
1946 t.set_cell_span (val_table_rect, std::move (content));
1949 table_cell_content make_cell_content_for_byte (style_manager &sm,
1950 unsigned char byte_val) const
1952 if (!m_show_utf8)
1954 if (byte_val == '\0')
1955 return styled_string (sm, "NUL");
1956 else if (byte_val < 0x80)
1957 if (ISPRINT (byte_val))
1958 return fmt_styled_string (sm, "%qc", byte_val);
1960 char buf[100];
1961 sprintf (buf, "0x%02x", byte_val);
1962 return styled_string (sm, buf);
1965 const string_region &m_string_reg;
1966 const theme &m_theme;
1967 const int m_ellipsis_threshold;
1968 const int m_ellipsis_head_len;
1969 const int m_ellipsis_tail_len;
1970 const bool m_show_full_string;
1971 const bool m_show_utf8;
1974 static std::unique_ptr<spatial_item>
1975 make_written_svalue_spatial_item (const access_operation &op,
1976 const svalue &sval,
1977 access_range actual_bits,
1978 const theme &theme)
1980 if (const initial_svalue *initial_sval = sval.dyn_cast_initial_svalue ())
1981 if (const string_region *string_reg
1982 = initial_sval->get_region ()->dyn_cast_string_region ())
1983 return make_unique <string_literal_spatial_item>
1984 (sval, actual_bits,
1985 *string_reg, theme,
1986 svalue_spatial_item::kind::WRITTEN);
1987 return make_unique <written_svalue_spatial_item> (op, sval, actual_bits);
1990 static std::unique_ptr<spatial_item>
1991 make_existing_svalue_spatial_item (const svalue *sval,
1992 const access_range &bits,
1993 const theme &theme)
1995 if (!sval)
1996 return nullptr;
1998 switch (sval->get_kind ())
2000 default:
2001 return nullptr;
2003 case SK_INITIAL:
2005 const initial_svalue *initial_sval = (const initial_svalue *)sval;
2006 if (const string_region *string_reg
2007 = initial_sval->get_region ()->dyn_cast_string_region ())
2008 return make_unique <string_literal_spatial_item>
2009 (*sval, bits,
2010 *string_reg, theme,
2011 svalue_spatial_item::kind::EXISTING);
2012 return nullptr;
2015 case SK_COMPOUND:
2016 return make_unique<compound_svalue_spatial_item>
2017 (*((const compound_svalue *)sval),
2018 bits,
2019 svalue_spatial_item::kind::EXISTING,
2020 theme);
2024 /* Widget subclass implementing access diagrams. */
2026 class access_diagram_impl : public vbox_widget
2028 public:
2029 access_diagram_impl (const access_operation &op,
2030 diagnostic_event_id_t region_creation_event_id,
2031 style_manager &sm,
2032 const theme &theme,
2033 logger *logger)
2034 : m_op (op),
2035 m_region_creation_event_id (region_creation_event_id),
2036 m_sm (sm),
2037 m_theme (theme),
2038 m_logger (logger),
2039 m_invalid (false),
2040 m_valid_region_spatial_item (op, region_creation_event_id, theme),
2041 m_accessed_region_spatial_item (op),
2042 m_btm (),
2043 m_calc_req_size_called (false)
2045 LOG_SCOPE (logger);
2047 if (logger)
2049 access_range invalid_before_bits;
2050 if (op.maybe_get_invalid_before_bits (&invalid_before_bits))
2051 invalid_before_bits.log ("invalid before range", *logger);
2052 access_range invalid_after_bits;
2053 if (op.maybe_get_invalid_after_bits (&invalid_after_bits))
2054 invalid_after_bits.log ("invalid after range", *logger);
2056 if (op.m_sval_hint)
2058 logger->start_log_line ();
2059 logger->log_partial ("sval_hint: ");
2060 op.m_sval_hint->dump_to_pp (logger->get_printer (), true);
2061 logger->end_log_line ();
2065 /* Register painting styles. */
2067 style valid_style (get_style_from_color_cap_name ("valid"));
2068 m_valid_style_id = m_sm.get_or_create_id (valid_style);
2070 style invalid_style (get_style_from_color_cap_name ("invalid"));
2071 m_invalid_style_id = m_sm.get_or_create_id (invalid_style);
2074 if (op.m_sval_hint)
2076 access_range actual_bits = m_op.get_actual_bits ();
2077 m_written_svalue_spatial_item
2078 = make_written_svalue_spatial_item (m_op,
2079 *op.m_sval_hint,
2080 actual_bits,
2081 m_theme);
2084 /* Two passes:
2085 First, figure out all of the boundaries of interest.
2086 Then use that to build child widgets showing the regions of interest,
2087 with a common tabular layout. */
2089 m_boundaries = find_boundaries ();
2090 if (logger)
2091 m_boundaries->log (*logger);
2093 /* Populate m_table_x_for_bit and m_bit_for_table_x.
2094 Each table column represents the range [offset, next_offset).
2095 We don't create a column in the table for the final offset, but we
2096 do populate it, so that looking at the table_x of one beyond the
2097 final table column gives us the upper bound offset. */
2098 m_btm.populate (*m_boundaries, *m_op.get_manager (), logger);
2100 /* Gracefully reject cases where the boundary sorting has gone wrong
2101 (due to awkward combinations of symbolic values). */
2103 table::range_t actual_bits_x_range
2104 = m_btm.get_table_x_for_range (m_op.get_actual_bits ());
2105 if (actual_bits_x_range.get_size () <= 0)
2107 if (logger)
2108 logger->log ("giving up: bad table columns for actual_bits");
2109 m_invalid = true;
2110 return;
2112 table::range_t valid_bits_x_range
2113 = m_btm.get_table_x_for_range (m_op.get_valid_bits ());
2114 if (valid_bits_x_range.get_size () <= 0)
2116 if (logger)
2117 logger->log ("giving up: bad table columns for valid_bits");
2118 m_invalid = true;
2119 return;
2123 m_col_widths
2124 = make_unique <table_dimension_sizes> (m_btm.get_num_columns ());
2126 /* Now create child widgets. */
2128 if (flag_analyzer_debug_text_art)
2130 table t_headings (make_headings_table ());
2131 add_aligned_child_table (std::move (t_headings));
2134 if (m_written_svalue_spatial_item)
2136 table t_sval (m_written_svalue_spatial_item->make_table (m_btm, m_sm));
2137 add_aligned_child_table (std::move (t_sval));
2139 else
2141 table t_accessed
2142 (m_accessed_region_spatial_item.make_table (m_btm, m_sm));
2143 add_aligned_child_table (std::move (t_accessed));
2146 add_direction_widget ();
2148 table t_valid (m_valid_region_spatial_item.make_table (m_btm, m_sm));
2149 add_invalid_accesses_to_region_table (t_valid);
2150 add_aligned_child_table (std::move (t_valid));
2152 add_valid_vs_invalid_ruler ();
2155 const char *get_desc () const override
2157 return "access_diagram_impl";
2160 canvas::size_t calc_req_size () final override
2162 if (m_invalid)
2163 return canvas::size_t (0, 0);
2165 /* Now compute the size requirements for the tables. */
2166 for (auto iter : m_aligned_table_widgets)
2167 iter->get_cell_sizes ().pass_1 (iter->get_table ());
2168 for (auto iter : m_aligned_table_widgets)
2169 iter->get_cell_sizes ().pass_2 (iter->get_table ());
2171 adjust_to_scale();
2173 /* ...and relayout the tables. */
2174 for (auto iter : m_aligned_table_widgets)
2175 iter->recalc_coords ();
2177 /* Populate the canvas_x per table_x. */
2178 m_col_start_x.clear ();
2179 int iter_canvas_x = 0;
2180 for (auto w : m_col_widths->m_requirements)
2182 m_col_start_x.push_back (iter_canvas_x);
2183 iter_canvas_x += w + 1;
2185 m_col_start_x.push_back (iter_canvas_x);
2187 m_calc_req_size_called = true;
2189 return vbox_widget::calc_req_size ();
2192 int get_canvas_x_for_table_x (int table_x) const
2194 gcc_assert (m_calc_req_size_called);
2195 return m_col_start_x[table_x];
2198 canvas::range_t get_canvas_x_range (const table::range_t &table_x_range) const
2200 gcc_assert (m_calc_req_size_called);
2201 return canvas::range_t (get_canvas_x_for_table_x (table_x_range.start),
2202 get_canvas_x_for_table_x (table_x_range.next));
2205 const access_operation &get_op () const { return m_op; }
2207 style::id_t get_style_id_for_validity (bool is_valid) const
2209 return is_valid ? m_valid_style_id : m_invalid_style_id;
2212 const theme &get_theme () const { return m_theme; }
2214 private:
2215 /* Figure out all of the boundaries of interest when visualizing ths op. */
2216 std::unique_ptr<boundaries>
2217 find_boundaries () const
2219 std::unique_ptr<boundaries> result
2220 = make_unique<boundaries> (*m_op.m_base_region, m_logger);
2222 m_valid_region_spatial_item.add_boundaries (*result, m_logger);
2223 m_accessed_region_spatial_item.add_boundaries (*result, m_logger);
2224 if (m_written_svalue_spatial_item)
2225 m_written_svalue_spatial_item->add_boundaries (*result, m_logger);
2227 return result;
2230 void add_aligned_child_table (table t)
2232 x_aligned_table_widget *w
2233 = new x_aligned_table_widget (std::move (t), m_theme, *m_col_widths);
2234 m_aligned_table_widgets.push_back (w);
2235 add_child (std::unique_ptr<widget> (w));
2238 /* Create a table showing headings for use by -fanalyzer-debug-text-art, for
2239 example:
2240 +---------+-----------+-----------+---+--------------------------------+
2241 | tc0 | tc1 | tc2 |tc3| tc4 |
2242 +---------+-----------+-----------+---+--------------------------------+
2243 |bytes 0-3|bytes 4-35 |bytes 36-39| | bytes 40-43 |
2244 +---------+-----------+-----------+ +--------------------------------+
2245 which has:
2246 - a row showing the table column numbers, labelled "tc0", "tc1", etc
2247 - a row showing the memory range of each table column that has one. */
2249 table make_headings_table () const
2251 table t (table::size_t (m_btm.get_num_columns (), 2));
2253 for (int table_x = 0; table_x < t.get_size ().w; table_x++)
2255 const int table_y = 0;
2256 t.set_cell (table::coord_t (table_x, table_y),
2257 fmt_styled_string (m_sm, "tc%i", table_x));
2259 for (int table_x = 0; table_x < t.get_size ().w; table_x++)
2261 const int table_y = 1;
2262 access_range range_for_column (NULL, bit_range (0, 0));
2263 if (m_btm.maybe_get_access_range_for_table_x (table_x,
2264 &range_for_column))
2266 pretty_printer pp;
2267 pp_format_decoder (&pp) = default_tree_printer;
2268 range_for_column.dump_to_pp (&pp, true);
2269 t.set_cell (table::coord_t (table_x, table_y),
2270 styled_string (m_sm, pp_formatted_text (&pp)));
2274 return t;
2277 void add_direction_widget ()
2279 add_child (::make_unique<direction_widget> (*this, m_btm));
2282 void add_invalid_accesses_to_region_table (table &t_region)
2284 gcc_assert (t_region.get_size ().w == (int)m_btm.get_num_columns ());
2286 const int table_y = 0;
2287 const int table_h = t_region.get_size ().h;
2289 access_range invalid_before_bits;
2290 if (m_op.maybe_get_invalid_before_bits (&invalid_before_bits))
2292 t_region.set_cell_span (m_btm.get_table_rect (invalid_before_bits,
2293 table_y, table_h),
2294 styled_string (m_sm,
2295 _("before valid range")));
2297 access_range invalid_after_bits;
2298 if (m_op.maybe_get_invalid_after_bits (&invalid_after_bits))
2300 t_region.set_cell_span (m_btm.get_table_rect (invalid_after_bits,
2301 table_y, table_h),
2302 styled_string (m_sm,
2303 _("after valid range")));
2307 void maybe_add_gap (x_aligned_x_ruler_widget *w,
2308 const access_range &lower,
2309 const access_range &upper) const
2311 LOG_SCOPE (m_logger);
2312 if (m_logger)
2314 lower.log ("lower", *m_logger);
2315 upper.log ("upper", *m_logger);
2317 region_model_manager *mgr = m_op.get_manager ();
2318 const svalue &lower_next = lower.m_next.calc_symbolic_bit_offset (mgr);
2319 const svalue &upper_start = upper.m_start.calc_symbolic_bit_offset (mgr);
2320 const svalue *num_bits_gap
2321 = mgr->get_or_create_binop (NULL_TREE, MINUS_EXPR,
2322 &upper_start, &lower_next);
2323 if (m_logger)
2324 m_logger->log ("num_bits_gap: %qs", num_bits_gap->get_desc ().get ());
2326 const svalue *zero = mgr->get_or_create_int_cst (NULL_TREE, 0);
2327 tristate ts_gt_zero = m_op.m_model.eval_condition (num_bits_gap,
2328 GT_EXPR,
2329 zero);
2330 if (ts_gt_zero.is_false ())
2332 if (m_logger)
2333 m_logger->log ("rejecting as not > 0");
2334 return;
2337 bit_size_expr num_bits (*num_bits_gap);
2338 if (auto p = num_bits.maybe_get_formatted_str (m_sm, m_op.m_model,
2339 _("%wi bit"),
2340 _("%wi bits"),
2341 _("%wi byte"),
2342 _("%wi bytes"),
2343 _("%qs bits"),
2344 _("%qs bytes")))
2346 styled_string label = std::move (*p.get ());
2347 w->add_range (m_btm.get_table_x_for_range
2348 (access_range (lower.m_next,
2349 upper.m_start,
2350 *mgr)),
2351 std::move (label),
2352 style::id_plain);
2356 styled_string
2357 make_warning_string (styled_string &&text)
2359 styled_string result;
2360 if (!m_theme.emojis_p ())
2361 return std::move (text);
2363 result.append (styled_string (0x26A0, /* U+26A0 WARNING SIGN. */
2364 true));
2365 /* U+26A0 WARNING SIGN has East_Asian_Width == Neutral, but in its
2366 emoji variant is printed (by vte at least) with a 2nd half
2367 overlapping the next char. Hence we add two spaces here: a space
2368 to be covered by this overlap, plus another space of padding. */
2369 result.append (styled_string (m_sm, " "));
2370 result.append (std::move (text));
2371 return result;
2374 /* Add a ruler child widet showing valid, invalid, and gaps. */
2375 void add_valid_vs_invalid_ruler ()
2377 LOG_SCOPE (m_logger);
2379 x_aligned_x_ruler_widget *w
2380 = new x_aligned_x_ruler_widget (*this, m_theme);
2382 access_range invalid_before_bits;
2383 if (m_op.maybe_get_invalid_before_bits (&invalid_before_bits))
2385 if (m_logger)
2386 invalid_before_bits.log ("invalid_before_bits", *m_logger);
2387 bit_size_expr num_before_bits
2388 (invalid_before_bits.get_size (m_op.get_manager ()));
2389 std::unique_ptr<styled_string> label;
2390 if (m_op.m_dir == DIR_READ)
2391 label = num_before_bits.maybe_get_formatted_str
2392 (m_sm, m_op.m_model,
2393 _("under-read of %wi bit"),
2394 _("under-read of %wi bits"),
2395 _("under-read of %wi byte"),
2396 _("under-read of %wi bytes"),
2397 _("under-read of %qs bits"),
2398 _("under-read of %qs bytes"));
2399 else
2400 label = num_before_bits.maybe_get_formatted_str
2401 (m_sm, m_op.m_model,
2402 _("underwrite of %wi bit"),
2403 _("underwrite of %wi bits"),
2404 _("underwrite of %wi byte"),
2405 _("underwrite of %wi bytes"),
2406 _("underwrite of %qs bits"),
2407 _("underwrite of %qs bytes"));
2408 if (label)
2409 w->add_range (m_btm.get_table_x_for_range (invalid_before_bits),
2410 make_warning_string (std::move (*label)),
2411 m_invalid_style_id);
2413 else
2415 if (m_logger)
2416 m_logger->log ("no invalid_before_bits");
2419 /* It would be nice to be able to use std::optional<access_range> here,
2420 but std::optional is C++17. */
2421 bool got_valid_bits = false;
2422 access_range valid_bits (m_op.get_valid_bits ());
2423 bit_size_expr num_valid_bits (valid_bits.get_size (m_op.get_manager ()));
2424 if (m_logger)
2425 valid_bits.log ("valid_bits", *m_logger);
2427 got_valid_bits = true;
2428 maybe_add_gap (w, invalid_before_bits, valid_bits);
2430 std::unique_ptr<styled_string> label;
2431 if (m_op.m_dir == DIR_READ)
2432 label = num_valid_bits.maybe_get_formatted_str (m_sm,
2433 m_op.m_model,
2434 _("size: %wi bit"),
2435 _("size: %wi bits"),
2436 _("size: %wi byte"),
2437 _("size: %wi bytes"),
2438 _("size: %qs bits"),
2439 _("size: %qs bytes"));
2440 else
2441 label
2442 = num_valid_bits.maybe_get_formatted_str (m_sm,
2443 m_op.m_model,
2444 _("capacity: %wi bit"),
2445 _("capacity: %wi bits"),
2446 _("capacity: %wi byte"),
2447 _("capacity: %wi bytes"),
2448 _("capacity: %qs bits"),
2449 _("capacity: %qs bytes"));
2450 if (label)
2451 w->add_range (m_btm.get_table_x_for_range (m_op.get_valid_bits ()),
2452 std::move (*label),
2453 m_valid_style_id);
2455 access_range invalid_after_bits;
2456 if (m_op.maybe_get_invalid_after_bits (&invalid_after_bits))
2458 if (got_valid_bits)
2459 maybe_add_gap (w, valid_bits, invalid_after_bits);
2461 if (m_logger)
2462 invalid_before_bits.log ("invalid_after_bits", *m_logger);
2464 bit_size_expr num_after_bits
2465 (invalid_after_bits.get_size (m_op.get_manager ()));
2466 std::unique_ptr<styled_string> label;
2467 if (m_op.m_dir == DIR_READ)
2468 label = num_after_bits.maybe_get_formatted_str
2469 (m_sm, m_op.m_model,
2470 _("over-read of %wi bit"),
2471 _("over-read of %wi bits"),
2472 _("over-read of %wi byte"),
2473 _("over-read of %wi bytes"),
2474 _("over-read of %qs bits"),
2475 _("over-read of %qs bytes"));
2476 else
2477 label = num_after_bits.maybe_get_formatted_str
2478 (m_sm, m_op.m_model,
2479 _("overflow of %wi bit"),
2480 _("overflow of %wi bits"),
2481 _("overflow of %wi byte"),
2482 _("overflow of %wi bytes"),
2483 _("overflow of %qs bits"),
2484 _("overflow of %qs bytes"));
2485 if (label)
2486 w->add_range (m_btm.get_table_x_for_range (invalid_after_bits),
2487 make_warning_string (std::move (*label)),
2488 m_invalid_style_id);
2490 else
2492 if (m_logger)
2493 m_logger->log ("no invalid_after_bits");
2496 add_child (std::unique_ptr<widget> (w));
2499 /* Subroutine of calc_req_size.
2500 Try to allocate surplus canvas width to table columns to make the
2501 per table-column canvas widths closer to being to scale.
2502 See e.g.:
2503 https://en.wikipedia.org/wiki/Fair_item_allocation
2504 https://en.wikipedia.org/wiki/Mathematics_of_apportionment
2506 void adjust_to_scale ()
2508 LOG_SCOPE (m_logger);
2509 const unsigned num_columns = m_btm.get_num_columns ();
2510 std::vector<bit_offset_t> bit_sizes (num_columns);
2511 for (unsigned table_x = 0; table_x < num_columns; table_x++)
2513 access_range range_for_column (NULL, bit_range (0, 0));
2514 if (m_btm.maybe_get_access_range_for_table_x (table_x,
2515 &range_for_column))
2517 bit_size_t size_in_bits;
2518 if (!range_for_column.get_size_in_bits (&size_in_bits))
2519 size_in_bits = BITS_PER_UNIT; // arbitrary non-zero value
2520 gcc_assert (size_in_bits > 0);
2521 bit_sizes[table_x] = size_in_bits;
2523 else
2524 bit_sizes[table_x] = 0;
2527 while (adjust_to_scale_once (bit_sizes))
2531 bool adjust_to_scale_once (const std::vector<bit_offset_t> &bit_sizes)
2533 LOG_SCOPE (m_logger);
2535 const unsigned num_columns = m_btm.get_num_columns ();
2537 /* Find the total canvas width currently required.
2538 Require one extra canvas column for the right-hand border
2539 of the table. */
2540 int total_width = 1;
2541 for (unsigned table_x = 0; table_x < num_columns; table_x++)
2543 int canvas_w = m_col_widths->m_requirements[table_x];
2544 gcc_assert (canvas_w >= 0);
2545 total_width += canvas_w + 1;
2548 const int max_width = param_analyzer_text_art_ideal_canvas_width;
2549 if (total_width >= max_width)
2551 if (m_logger)
2552 m_logger->log ("bailing out: total_width=%i ,>= max_width (%i)\n",
2553 total_width, max_width);
2554 return false;
2557 const int fixed_point = 1024;
2558 std::vector<bit_offset_t> canvas_w_per_bit (num_columns);
2559 for (unsigned table_x = 0; table_x < num_columns; table_x++)
2561 bit_offset_t bit_size = bit_sizes[table_x];
2562 if (bit_size > 0)
2563 canvas_w_per_bit[table_x]
2564 = (m_col_widths->m_requirements[table_x] * fixed_point) / bit_size;
2565 else
2566 canvas_w_per_bit[table_x] = INT_MAX;
2569 /* Find the min canvas per bit, and give an extra canvas column to
2570 the table column that has least. */
2571 size_t min_idx = std::distance (canvas_w_per_bit.begin (),
2572 std::min_element (canvas_w_per_bit.begin (),
2573 canvas_w_per_bit.end ()));
2574 m_col_widths->m_requirements[min_idx] += 1;
2575 if (m_logger)
2576 m_logger->log ("adding 1 canvas_w to column %i\n", (int)min_idx);
2578 return true; // keep going
2581 const access_operation &m_op;
2582 diagnostic_event_id_t m_region_creation_event_id;
2583 style_manager &m_sm;
2584 const theme &m_theme;
2585 logger *m_logger;
2586 /* In lieu of being able to throw exceptions, a flag to mark this object
2587 as "invalid". */
2588 bool m_invalid;
2590 style::id_t m_valid_style_id;
2591 style::id_t m_invalid_style_id;
2593 valid_region_spatial_item m_valid_region_spatial_item;
2594 accessed_region_spatial_item m_accessed_region_spatial_item;
2595 std::unique_ptr<spatial_item> m_written_svalue_spatial_item;
2597 std::unique_ptr<boundaries> m_boundaries;
2599 bit_to_table_map m_btm;
2601 bool m_calc_req_size_called;
2603 /* Column widths shared by all x_aligned_table_widget,
2604 created once we know how many columns we need. */
2605 std::unique_ptr<table_dimension_sizes> m_col_widths;
2607 /* All of the child x_aligned_table_widget that share
2608 column widths. */
2609 std::vector<x_aligned_table_widget *> m_aligned_table_widgets;
2611 /* Mapping from table_x to canvas_x. */
2612 std::vector<int> m_col_start_x;
2615 x_ruler
2616 x_aligned_x_ruler_widget::make_x_ruler () const
2618 x_ruler r (x_ruler::label_dir::BELOW);
2619 for (auto& iter : m_labels)
2621 canvas::range_t canvas_x_range
2622 = m_dia_impl.get_canvas_x_range (iter.m_table_x_range);
2623 /* Include the end-point. */
2624 canvas_x_range.next++;
2625 r.add_label (canvas_x_range, iter.m_text.copy (), iter.m_style_id,
2626 x_ruler::label_kind::TEXT_WITH_BORDER);
2628 return r;
2631 /* class direction_widget : public leaf_widget. */
2633 /* Paint arrows indicating the direction of the access (read vs write),
2634 but only in the X-extent corresponding to the region that's actually
2635 accessed. */
2637 void
2638 direction_widget::paint_to_canvas (canvas &canvas)
2640 const access_range accessed_bits (m_dia_impl.get_op ().get_actual_bits ());
2642 const access_range valid_bits (m_dia_impl.get_op ().get_valid_bits ());
2644 for (unsigned table_x = 0; table_x < m_btm.get_num_columns (); table_x++)
2646 access_range column_access_range;
2647 if (m_btm.maybe_get_access_range_for_table_x (table_x,
2648 &column_access_range))
2650 /* Only paint arrows in the accessed region. */
2651 if (!accessed_bits.contains_p (column_access_range))
2652 continue;
2654 /* Are we within the valid region? */
2655 const bool is_valid (valid_bits.contains_p (column_access_range));
2656 const style::id_t style_id
2657 = m_dia_impl.get_style_id_for_validity (is_valid);
2658 const canvas::range_t x_canvas_range
2659 = m_dia_impl.get_canvas_x_range (table::range_t (table_x,
2660 table_x + 1));
2661 const int canvas_x = x_canvas_range.get_midpoint ();
2662 m_dia_impl.get_theme ().paint_y_arrow
2663 (canvas,
2664 canvas_x,
2665 canvas::range_t (get_y_range ()),
2666 (m_dia_impl.get_op ().m_dir == DIR_READ
2667 ? theme::y_arrow_dir::UP
2668 : theme::y_arrow_dir::DOWN),
2669 style_id);
2674 /* class access_diagram : public text_art::wrapper_widget. */
2676 /* To hide the implementation details, this is merely a wrapper around
2677 an access_diagram_impl. */
2679 access_diagram::access_diagram (const access_operation &op,
2680 diagnostic_event_id_t region_creation_event_id,
2681 style_manager &sm,
2682 const theme &theme,
2683 logger *logger)
2684 : wrapper_widget (make_unique <access_diagram_impl> (op,
2685 region_creation_event_id,
2687 theme,
2688 logger))
2692 #if CHECKING_P
2694 namespace selftest {
2696 /* Implementation detail of ASSERT_EQ_TYPELESS_INTEGER. */
2698 static void
2699 assert_eq_typeless_integer (const location &loc,
2700 const svalue *sval,
2701 int expected_int_val)
2703 ASSERT_NE_AT (loc, sval, nullptr);
2704 ASSERT_EQ_AT (loc, sval->get_kind (), SK_CONSTANT);
2705 ASSERT_EQ_AT (loc,
2706 wi::to_offset (sval->maybe_get_constant ()),
2707 expected_int_val);
2708 ASSERT_EQ_AT (loc, sval->get_type (), NULL_TREE);
2711 /* Assert that SVAL is a constant_svalue equal to EXPECTED_INT_VAL,
2712 with NULL_TREE as its type. */
2714 #define ASSERT_EQ_TYPELESS_INTEGER(SVAL, EXPECTED_INT_VAL) \
2715 SELFTEST_BEGIN_STMT \
2716 assert_eq_typeless_integer ((SELFTEST_LOCATION), \
2717 (SVAL), \
2718 (EXPECTED_INT_VAL)); \
2719 SELFTEST_END_STMT
2722 /* Various tests of bit_size_expr::maybe_get_as_bytes. */
2724 static void
2725 test_bit_size_expr_to_bytes ()
2727 region_model_manager mgr;
2729 /* 40 bits: should be 5 bytes. */
2731 bit_size_expr num_bits (*mgr.get_or_create_int_cst (NULL_TREE, 40));
2732 const svalue *as_bytes = num_bits.maybe_get_as_bytes (mgr);
2733 ASSERT_EQ_TYPELESS_INTEGER (as_bytes, 5);
2736 /* 41 bits: should not convert to bytes. */
2738 bit_size_expr num_bits (*mgr.get_or_create_int_cst (NULL_TREE, 41));
2739 const svalue *as_bytes = num_bits.maybe_get_as_bytes (mgr);
2740 ASSERT_EQ (as_bytes, nullptr);
2743 tree n = build_global_decl ("n", size_type_node);
2745 const svalue *init_n
2746 = mgr.get_or_create_initial_value (mgr.get_region_for_global (n));
2748 const svalue *n_times_8
2749 = mgr.get_or_create_binop (NULL_TREE, MULT_EXPR,
2750 init_n,
2751 mgr.get_or_create_int_cst (NULL_TREE, 8));
2753 /* (n * 8) bits should be n bytes */
2755 bit_size_expr num_bits (*n_times_8);
2756 const svalue *as_bytes = num_bits.maybe_get_as_bytes (mgr);
2757 ASSERT_EQ (as_bytes, mgr.get_or_create_cast (NULL_TREE, init_n));
2760 /* (n * 8) + 16 bits should be n + 2 bytes */
2762 bit_size_expr num_bits
2763 (*mgr.get_or_create_binop (NULL_TREE, PLUS_EXPR,
2764 n_times_8,
2765 mgr.get_or_create_int_cst (NULL_TREE, 16)));
2766 const svalue *as_bytes = num_bits.maybe_get_as_bytes (mgr);
2767 ASSERT_EQ (as_bytes->get_kind (), SK_BINOP);
2768 const binop_svalue *binop = as_bytes->dyn_cast_binop_svalue ();
2769 ASSERT_EQ (binop->get_op (), PLUS_EXPR);
2770 ASSERT_EQ (binop->get_arg0 (), mgr.get_or_create_cast (NULL_TREE, init_n));
2771 ASSERT_EQ_TYPELESS_INTEGER (binop->get_arg1 (), 2);
2775 /* Run all of the selftests within this file. */
2777 void
2778 analyzer_access_diagram_cc_tests ()
2780 test_bit_size_expr_to_bytes ();
2783 } // namespace selftest
2785 #endif /* CHECKING_P */
2787 } // namespace ana
2789 #endif /* #if ENABLE_ANALYZER */