Line data Source code
1 : /* Text art visualizations within -fanalyzer.
2 : Copyright (C) 2023-2026 Free Software Foundation, Inc.
3 :
4 : This file is part of GCC.
5 :
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.
10 :
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.
15 :
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/>. */
19 :
20 : #define INCLUDE_ALGORITHM
21 : #define INCLUDE_MAP
22 : #define INCLUDE_SET
23 : #include "analyzer/common.h"
24 :
25 : #include "fold-const.h"
26 : #include "intl.h"
27 :
28 : #include "text-art/ruler.h"
29 :
30 : #include "analyzer/region-model.h"
31 : #include "analyzer/access-diagram.h"
32 : #include "analyzer/analyzer-selftests.h"
33 :
34 : #if ENABLE_ANALYZER
35 :
36 : /* Consider this code:
37 : int32_t arr[10];
38 : arr[10] = x;
39 : where we've emitted a buffer overflow diagnostic like this:
40 : out-of-bounds write from byte 40 till byte 43 but 'arr' ends at byte 40
41 :
42 : We want to emit a diagram that visualizes:
43 : - the spatial relationship between the valid region to access, versus
44 : the region that was actually accessed: does it overlap, was it touching,
45 : close, or far away? Was it before or after in memory? What are the
46 : relative sizes involved?
47 : - the direction of the access (read vs write)
48 :
49 : The following code supports emitting diagrams similar to the following:
50 :
51 : # +--------------------------------+
52 : # |write from ‘x’ (type: ‘int32_t’)|
53 : # +--------------------------------+
54 : # |
55 : # |
56 : # v
57 : # +---------+-----------+-----------+ +--------------------------------+
58 : # | [0] | ... | [9] | | after valid range |
59 : # +---------+-----------+-----------+ | |
60 : # | ‘arr’ (type: ‘int32_t[10]’) | | |
61 : # +---------------------------------+ +--------------------------------+
62 : # |~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~|
63 : # | |
64 : # +---------+--------+ +---------+---------+
65 : # |capacity: 40 bytes| |overflow of 4 bytes|
66 : # +------------------+ +-------------------+
67 :
68 : where the diagram is laid out via table columns where each table column
69 : represents either a range of bits/bytes, or is a spacing column (to highlight
70 : the boundary between valid vs invalid accesses). The table columns can be
71 : seen via -fanalyzer-debug-text-art. For example, here there are 5 table
72 : columns ("tc0" through "tc4"):
73 :
74 : # +---------+-----------+-----------+---+--------------------------------+
75 : # | tc0 | tc1 | tc2 |tc3| tc4 |
76 : # +---------+-----------+-----------+---+--------------------------------+
77 : # |bytes 0-3|bytes 4-35 |bytes 36-39| | bytes 40-43 |
78 : # +---------+-----------+-----------+ +--------------------------------+
79 : #
80 : # +--------------------------------+
81 : # |write from ‘x’ (type: ‘int32_t’)|
82 : # +--------------------------------+
83 : # |
84 : # |
85 : # v
86 : # +---------+-----------+-----------+ +--------------------------------+
87 : # | [0] | ... | [9] | | after valid range |
88 : # +---------+-----------+-----------+ | |
89 : # | ‘arr’ (type: ‘int32_t[10]’) | | |
90 : # +---------------------------------+ +--------------------------------+
91 : # |~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~|
92 : # | |
93 : # +---------+--------+ +---------+---------+
94 : # |capacity: 40 bytes| |overflow of 4 bytes|
95 : # +------------------+ +-------------------+
96 :
97 : The diagram is built up from the following:
98 :
99 : # +--------------------------------+
100 : # | ITEM FOR SVALUE/ACCESSED REGION|
101 : # +--------------------------------+
102 : # |
103 : # | DIRECTION WIDGET
104 : # v
105 : # +---------------------------------+ +--------------------------------+
106 : # | VALID REGION | | INVALID ACCESS |
107 : # +---------------------------------+ +--------------------------------+
108 : #
109 : # | VALID-VS-INVALID RULER |
110 :
111 : i.e. a vbox_widget containing 4 child widgets laid out vertically:
112 : - ALIGNED CHILD WIDGET: ITEM FOR SVALUE/ACCESSED REGION
113 : - DIRECTION WIDGET
114 : - ALIGNED CHILD WIDGET: VALID AND INVALID ACCESSES
115 : - VALID-VS-INVALID RULER.
116 :
117 : A more complicated example, given this overflow:
118 : char buf[100];
119 : strcpy (buf, LOREM_IPSUM);
120 :
121 : 01| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
122 : 02| |[0]|[1]|[2]|[3]|[4]|[5]| ... |[440]|[441]|[442]|[443]|[444]|[445]|
123 : 03| +---+---+---+---+---+---+ +-----+-----+-----+-----+-----+-----+
124 : 04| |'L'|'o'|'r'|'e'|'m'|' '| | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
125 : 05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
126 : 06| | string literal (type: 'char[446]') |
127 : 07| +----------------------------------------------------------------------+
128 : 08| | | | | | | | | | | | | | | |
129 : 09| | | | | | | | | | | | | | | |
130 : 10| v v v v v v v v v v v v v v v
131 : 11| +---+---------------------+----++--------------------------------------+
132 : 12| |[0]| ... |[99]|| after valid range |
133 : 13| +---+---------------------+----+| |
134 : 14| | 'buf' (type: 'char[100]') || |
135 : 15| +------------------------------++--------------------------------------+
136 : 16| |~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
137 : 17| | |
138 : 18| +---------+---------+ +----------+----------+
139 : 19| |capacity: 100 bytes| |overflow of 346 bytes|
140 : 20| +-------------------+ +---------------------+
141 :
142 : which is:
143 :
144 : 01| ALIGNED CHILD WIDGET (lines 01-07): (string_region_spatial_item)-+-----+
145 : 02| |[0]|[1]|[2]|[3]|[4]|[5]| ... |[440]|[441]|[442]|[443]|[444]|[445]|
146 : 03| +---+---+---+---+---+---+ +-----+-----+-----+-----+-----+-----+
147 : 04| |'L'|'o'|'r'|'e'|'m'|' '| | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
148 : 05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
149 : 06| | string literal (type: 'char[446]') |
150 : 07| +----------------------------------------------------------------------+
151 : 08| DIRECTION WIDGET (lines 08-10) | | | | | | |
152 : 09| | | | | | | | | | | | | | | |
153 : 10| v v v v v v v v v v v v v v v
154 : 11| ALIGNED CHILD WIDGET (lines 11-15)-------------------------------------+
155 : 12| VALID REGION ... |[99]|| INVALID ACCESS |
156 : 13| +---+---------------------+----+| |
157 : 14| | 'buf' (type: 'char[100]') || |
158 : 15| +------------------------------++--------------------------------------+
159 : 16| VALID-VS-INVALID RULER (lines 16-20): ~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
160 : 17| | |
161 : 18| +---------+---------+ +----------+----------+
162 : 19| |capacity: 100 bytes| |overflow of 346 bytes|
163 : 20| +-------------------+ +---------------------+
164 :
165 : We build the diagram in several phases:
166 : - (1) we construct an access_diagram_impl widget. Within the ctor, we have
167 : these subphases:
168 : - (1.1) find all of the boundaries of interest
169 : - (1.2) use the boundaries to build a bit_table_map, associating bit ranges
170 : with table columns (e.g. "byte 0 is column 0, bytes 1-98 are column 2" etc)
171 : - (1.3) create child widgets that share this table-based geometry
172 : - (2) ask the widget for its size request
173 : - (2.1) column widths and row heights for the table are computed by
174 : access_diagram_impl::calc_req_size
175 : - (2.2) child widgets request sizes based on these widths/heights
176 : - (3) create a canvas of the appropriate size
177 : - (4) paint the widget hierarchy to the canvas. */
178 :
179 :
180 : using namespace text_art;
181 :
182 : namespace ana {
183 :
184 : static styled_string
185 : fmt_styled_string (style_manager &sm,
186 : const char *fmt, ...)
187 : ATTRIBUTE_GCC_DIAG(2, 3);
188 :
189 : static styled_string
190 760 : fmt_styled_string (style_manager &sm,
191 : const char *fmt, ...)
192 : {
193 760 : va_list ap;
194 760 : va_start (ap, fmt);
195 760 : styled_string result
196 760 : = styled_string::from_fmt_va (sm, default_tree_printer, fmt, &ap);
197 760 : va_end (ap);
198 760 : return result;
199 : }
200 :
201 : class access_diagram_impl;
202 : class bit_to_table_map;
203 :
204 : static void
205 10 : pp_bit_size_t (pretty_printer *pp, bit_size_t num_bits)
206 : {
207 10 : if (num_bits % BITS_PER_UNIT == 0)
208 : {
209 10 : byte_size_t num_bytes = num_bits / BITS_PER_UNIT;
210 10 : if (num_bytes == 1)
211 0 : pp_printf (pp, _("%wi byte"), num_bytes.to_uhwi ());
212 : else
213 10 : pp_printf (pp, _("%wi bytes"), num_bytes.to_uhwi ());
214 : }
215 : else
216 : {
217 0 : if (num_bits == 1)
218 0 : pp_printf (pp, _("%wi bit"), num_bits.to_uhwi ());
219 : else
220 0 : pp_printf (pp, _("%wi bits"), num_bits.to_uhwi ());
221 : }
222 10 : }
223 :
224 : static styled_string
225 25 : get_access_size_str (style_manager &sm,
226 : const access_operation &op,
227 : access_range accessed_range,
228 : tree type)
229 : {
230 25 : bit_size_expr num_bits (accessed_range.get_size (op.m_model.get_manager ()));
231 25 : if (type)
232 : {
233 10 : styled_string s;
234 10 : pretty_printer pp;
235 10 : pp_format_decoder (&pp) = default_tree_printer;
236 10 : if (num_bits.maybe_print_for_user (&pp, op.m_model))
237 : {
238 10 : if (op.m_dir == access_direction::read)
239 6 : return fmt_styled_string (sm,
240 6 : _("read of %qT (%s)"),
241 : type,
242 6 : pp_formatted_text (&pp));
243 : else
244 4 : return fmt_styled_string (sm,
245 4 : _("write of %qT (%s)"),
246 : type,
247 4 : pp_formatted_text (&pp));
248 : }
249 10 : }
250 15 : if (op.m_dir == access_direction::read)
251 : {
252 11 : if (auto p
253 : = num_bits.maybe_get_formatted_str (sm, op.m_model,
254 11 : _("read of %wi bit"),
255 11 : _("read of %wi bits"),
256 11 : _("read of %wi byte"),
257 11 : _("read of %wi bytes"),
258 11 : _("read of %qs bits"),
259 11 : _("read of %qs bytes")))
260 11 : return std::move (*p.get ());
261 : }
262 : else
263 : {
264 4 : if (auto p
265 : = num_bits.maybe_get_formatted_str (sm, op.m_model,
266 4 : _("write of %wi bit"),
267 4 : _("write of %wi bits"),
268 4 : _("write of %wi byte"),
269 4 : _("write of %wi bytes"),
270 4 : _("write of %qs bits"),
271 4 : _("write of %qs bytes")))
272 4 : return std::move (*p.get ());
273 : }
274 :
275 0 : if (type)
276 : {
277 0 : if (op.m_dir == access_direction::read)
278 0 : return fmt_styled_string (sm, _("read of %qT"), type);
279 : else
280 0 : return fmt_styled_string (sm, _("write of %qT"), type);
281 : }
282 :
283 0 : if (op.m_dir == access_direction::read)
284 0 : return styled_string (sm, _("read"));
285 : else
286 0 : return styled_string (sm, _("write"));
287 : }
288 :
289 : /* Subroutine of clean_up_for_diagram. */
290 :
291 : static tree
292 0 : strip_any_cast (tree expr)
293 : {
294 0 : if (TREE_CODE (expr) == NOP_EXPR
295 0 : || TREE_CODE (expr) == NON_LVALUE_EXPR)
296 0 : expr = TREE_OPERAND (expr, 0);
297 0 : return expr;
298 : }
299 :
300 : /* Duplicate EXPR, replacing any SSA names with the underlying variable. */
301 :
302 : tree
303 4 : remove_ssa_names (tree expr)
304 : {
305 4 : if (TREE_CODE (expr) == SSA_NAME
306 4 : && SSA_NAME_VAR (expr))
307 : return SSA_NAME_VAR (expr);
308 0 : tree t = copy_node (expr);
309 0 : for (int i = 0; i < TREE_OPERAND_LENGTH (expr); i++)
310 0 : if (TREE_OPERAND (expr, i))
311 0 : TREE_OPERAND (t, i) = remove_ssa_names (TREE_OPERAND (expr, i));
312 : return t;
313 : }
314 :
315 : /* We want to be able to print tree expressions from the analyzer,
316 : which is in the middle end.
317 :
318 : We could use the front-end pretty_printer's formatting routine,
319 : but:
320 : (a) some have additional state in a pretty_printer subclass, so we'd
321 : need to clone global_dc->printer
322 : (b) the "aka" type information added by the C and C++ frontends are
323 : too verbose when building a diagram, and there isn't a good way to ask
324 : for a less verbose version of them.
325 :
326 : Hence we use default_tree_printer.
327 : However, we want to avoid printing SSA names, and instead print the
328 : underlying var name.
329 : Ideally there would be a better tree printer for use by middle end
330 : warnings, but as workaround, this function clones a tree, replacing
331 : SSA names with the var names. */
332 :
333 : tree
334 0 : clean_up_for_diagram (tree expr)
335 : {
336 0 : tree without_ssa_names = remove_ssa_names (expr);
337 0 : return strip_any_cast (without_ssa_names);
338 : }
339 :
340 : /* struct bit_size_expr. */
341 :
342 : /* Attempt to generate a user-facing styled string that mentions this
343 : bit_size_expr.
344 : Use MODEL for extracting representative tree values where necessary.
345 : The CONCRETE_* format strings should contain a single %wi.
346 : The SYMBOLIC_* format strings should contain a single %qs.
347 : Return nullptr if unable to represent the expression. */
348 :
349 : std::unique_ptr<text_art::styled_string>
350 169 : bit_size_expr::maybe_get_formatted_str (text_art::style_manager &sm,
351 : const region_model &model,
352 : const char *concrete_single_bit_fmt,
353 : const char *concrete_plural_bits_fmt,
354 : const char *concrete_single_byte_fmt,
355 : const char *concrete_plural_bytes_fmt,
356 : const char *symbolic_bits_fmt,
357 : const char *symbolic_bytes_fmt) const
358 : {
359 169 : region_model_manager &mgr = *model.get_manager ();
360 169 : if (const svalue *num_bytes = maybe_get_as_bytes (mgr))
361 : {
362 168 : if (tree cst = num_bytes->maybe_get_constant ())
363 : {
364 136 : byte_size_t concrete_num_bytes = wi::to_offset (cst);
365 136 : if (!wi::fits_uhwi_p (concrete_num_bytes))
366 0 : return nullptr;
367 136 : if (concrete_num_bytes == 1)
368 11 : return std::make_unique <text_art::styled_string>
369 22 : (fmt_styled_string (sm, concrete_single_byte_fmt,
370 11 : concrete_num_bytes.to_uhwi ()));
371 : else
372 125 : return std::make_unique <text_art::styled_string>
373 250 : (fmt_styled_string (sm, concrete_plural_bytes_fmt,
374 125 : concrete_num_bytes.to_uhwi ()));
375 : }
376 : else
377 : {
378 32 : pretty_printer pp;
379 32 : pp_format_decoder (&pp) = default_tree_printer;
380 32 : if (!num_bytes->maybe_print_for_user (&pp, model))
381 0 : return nullptr;
382 32 : return std::make_unique <text_art::styled_string>
383 64 : (fmt_styled_string (sm, symbolic_bytes_fmt,
384 32 : pp_formatted_text (&pp)));
385 32 : }
386 : }
387 1 : else if (tree cst = m_num_bits.maybe_get_constant ())
388 : {
389 0 : bit_size_t concrete_num_bits = wi::to_offset (cst);
390 0 : if (!wi::fits_uhwi_p (concrete_num_bits))
391 0 : return nullptr;
392 0 : if (concrete_num_bits == 1)
393 0 : return std::make_unique <text_art::styled_string>
394 0 : (fmt_styled_string (sm, concrete_single_bit_fmt,
395 0 : concrete_num_bits.to_uhwi ()));
396 : else
397 0 : return std::make_unique <text_art::styled_string>
398 0 : (fmt_styled_string (sm, concrete_plural_bits_fmt,
399 0 : concrete_num_bits.to_uhwi ()));
400 : }
401 : else
402 : {
403 1 : pretty_printer pp;
404 1 : pp_format_decoder (&pp) = default_tree_printer;
405 1 : if (!m_num_bits.maybe_print_for_user (&pp, model))
406 1 : return nullptr;
407 0 : return std::make_unique <text_art::styled_string>
408 0 : (fmt_styled_string (sm, symbolic_bits_fmt,
409 0 : pp_formatted_text (&pp)));
410 1 : }
411 : }
412 :
413 : bool
414 10 : bit_size_expr::maybe_print_for_user (pretty_printer *pp,
415 : const region_model &model) const
416 : {
417 10 : if (tree cst = m_num_bits.maybe_get_constant ())
418 : {
419 10 : bit_size_t concrete_num_bits = wi::to_offset (cst);
420 10 : pp_bit_size_t (pp, concrete_num_bits);
421 10 : return true;
422 : }
423 : else
424 : {
425 0 : if (const svalue *num_bytes = maybe_get_as_bytes (*model.get_manager ()))
426 : {
427 0 : pretty_printer tmp_pp;
428 0 : pp_format_decoder (&tmp_pp) = default_tree_printer;
429 0 : if (!num_bytes->maybe_print_for_user (&tmp_pp, model))
430 : return false;
431 0 : pp_printf (pp, _("%qs bytes"), pp_formatted_text (&tmp_pp));
432 0 : return true;
433 0 : }
434 : else
435 : {
436 0 : pretty_printer tmp_pp;
437 0 : pp_format_decoder (&tmp_pp) = default_tree_printer;
438 0 : if (!m_num_bits.maybe_print_for_user (&tmp_pp, model))
439 : return false;
440 0 : pp_printf (pp, _("%qs bits"), pp_formatted_text (&tmp_pp));
441 0 : return true;
442 0 : }
443 : }
444 : }
445 :
446 : /* Attempt to get a symbolic value for this symbolic bit size,
447 : expressed in bytes.
448 : Return null if it's not known to divide exactly. */
449 :
450 : const svalue *
451 185 : bit_size_expr::maybe_get_as_bytes (region_model_manager &mgr) const
452 : {
453 185 : if (tree cst = m_num_bits.maybe_get_constant ())
454 : {
455 144 : bit_offset_t concrete_bits = wi::to_offset (cst);
456 144 : if (concrete_bits % BITS_PER_UNIT != 0)
457 : /* Not an exact multiple, so fail. */
458 4 : return nullptr;
459 : }
460 181 : const svalue *bits_per_byte
461 181 : = mgr.get_or_create_int_cst (NULL_TREE, BITS_PER_UNIT);
462 181 : return mgr.maybe_fold_binop (NULL_TREE, EXACT_DIV_EXPR,
463 181 : &m_num_bits, bits_per_byte);
464 : }
465 :
466 : /* struct access_range. */
467 :
468 1130 : access_range::access_range (const region *base_region, const bit_range &bits)
469 1130 : : m_start (region_offset::make_concrete (base_region,
470 : bits.get_start_bit_offset ())),
471 1130 : m_next (region_offset::make_concrete (base_region,
472 : bits.get_next_bit_offset ()))
473 : {
474 1130 : }
475 :
476 0 : access_range::access_range (const region *base_region, const byte_range &bytes)
477 0 : : m_start (region_offset::make_concrete (base_region,
478 : bytes.get_start_bit_offset ())),
479 0 : m_next (region_offset::make_concrete (base_region,
480 : bytes.get_next_bit_offset ()))
481 : {
482 0 : }
483 :
484 657 : access_range::access_range (const region ®, region_model_manager *mgr)
485 657 : : m_start (strip_types (reg.get_offset (mgr), *mgr)),
486 657 : m_next (strip_types (reg.get_next_offset (mgr), *mgr))
487 : {
488 657 : }
489 :
490 : bit_size_expr
491 165 : access_range::get_size (region_model_manager *mgr) const
492 : {
493 165 : const svalue &start_bit_offset = m_start.calc_symbolic_bit_offset (mgr);
494 165 : const svalue &next_bit_offset = m_next.calc_symbolic_bit_offset (mgr);
495 165 : return bit_size_expr
496 165 : (*mgr->get_or_create_binop (NULL_TREE, MINUS_EXPR,
497 165 : &next_bit_offset, &start_bit_offset));
498 : }
499 :
500 : bool
501 735 : access_range::contains_p (const access_range &other) const
502 : {
503 735 : return (m_start <= other.m_start
504 735 : && other.m_next <= m_next);
505 : }
506 :
507 : bool
508 554 : access_range::empty_p () const
509 : {
510 554 : bit_range concrete_bits (0, 0);
511 554 : if (!as_concrete_bit_range (&concrete_bits))
512 : return false;
513 460 : return concrete_bits.empty_p ();
514 : }
515 :
516 : void
517 4 : access_range::dump_to_pp (pretty_printer *pp, bool simple) const
518 : {
519 4 : if (m_start.concrete_p () && m_next.concrete_p ())
520 : {
521 4 : bit_range bits (m_start.get_bit_offset (),
522 4 : m_next.get_bit_offset () - m_start.get_bit_offset ());
523 4 : bits.dump_to_pp (pp);
524 4 : return;
525 : }
526 0 : pp_character (pp, '[');
527 0 : m_start.dump_to_pp (pp, simple);
528 0 : pp_string (pp, " to ");
529 0 : m_next.dump_to_pp (pp, simple);
530 0 : pp_character (pp, ')');
531 : }
532 :
533 : DEBUG_FUNCTION void
534 0 : access_range::dump (bool simple) const
535 : {
536 0 : tree_dump_pretty_printer pp (stderr);
537 0 : dump_to_pp (&pp, simple);
538 0 : pp_newline (&pp);
539 0 : }
540 :
541 : void
542 0 : access_range::log (const char *title, logger &logger) const
543 : {
544 0 : logger.start_log_line ();
545 0 : logger.log_partial ("%s: ", title);
546 0 : dump_to_pp (logger.get_printer (), true);
547 0 : logger.end_log_line ();
548 0 : }
549 :
550 : /* struct access_operation. */
551 :
552 : access_range
553 1309 : access_operation::get_valid_bits () const
554 : {
555 1309 : const svalue *capacity_in_bytes_sval = m_model.get_capacity (m_base_region);
556 1309 : return access_range
557 1309 : (region_offset::make_concrete (m_base_region, 0),
558 1309 : region_offset::make_byte_offset (m_base_region, capacity_in_bytes_sval),
559 2618 : *get_manager ());
560 : }
561 :
562 : access_range
563 581 : access_operation::get_actual_bits () const
564 : {
565 581 : return access_range (m_reg, get_manager ());
566 : }
567 :
568 : /* If there are any bits accessed invalidly before the valid range,
569 : return true and write their range to *OUT.
570 : Return false if there aren't, or if there's a problem
571 : (e.g. symbolic ranges. */
572 :
573 : bool
574 136 : access_operation::maybe_get_invalid_before_bits (access_range *out) const
575 : {
576 136 : access_range valid_bits (get_valid_bits ());
577 136 : access_range actual_bits (get_actual_bits ());
578 :
579 136 : if (actual_bits.m_start >= valid_bits.m_start)
580 : {
581 : /* No part of accessed range is before the valid range. */
582 : return false;
583 : }
584 30 : else if (actual_bits.m_next > valid_bits.m_start)
585 : {
586 : /* Get part of accessed range that's before the valid range. */
587 8 : *out = access_range (actual_bits.m_start, valid_bits.m_start,
588 8 : *get_manager ());
589 8 : return true;
590 : }
591 : else
592 : {
593 : /* Accessed range is fully before valid range. */
594 22 : *out = actual_bits;
595 22 : return true;
596 : }
597 : }
598 :
599 : /* If there are any bits accessed invalidly after the valid range,
600 : return true and write their range to *OUT.
601 : Return false if there aren't, or if there's a problem. */
602 :
603 : bool
604 136 : access_operation::maybe_get_invalid_after_bits (access_range *out) const
605 : {
606 136 : access_range valid_bits (get_valid_bits ());
607 136 : access_range actual_bits (get_actual_bits ());
608 :
609 136 : if (actual_bits.m_next <= valid_bits.m_next)
610 : {
611 : /* No part of accessed range is after the valid range. */
612 : return false;
613 : }
614 114 : else if (actual_bits.m_start < valid_bits.m_next)
615 : {
616 : /* Get part of accessed range that's after the valid range. */
617 56 : *out = access_range (valid_bits.m_next, actual_bits.m_next,
618 56 : *get_manager ());
619 56 : return true;
620 : }
621 : else
622 : {
623 : /* Accessed range is fully after valid range. */
624 58 : *out = actual_bits;
625 58 : return true;
626 : }
627 : }
628 :
629 : /* A class for capturing all of the region offsets of interest (both concrete
630 : and symbolic), to help align everything in the diagram.
631 : Boundaries can be soft or hard; hard boundaries are emphasized visually
632 : (e.g. the boundary between valid vs invalid accesses).
633 :
634 : Offsets in the boundaries are all expressed relative to the base
635 : region of the access_operation. */
636 :
637 : class boundaries
638 : {
639 : public:
640 : enum class kind { HARD, SOFT};
641 :
642 72 : boundaries (const region &base_reg, logger *logger)
643 72 : : m_base_reg (base_reg), m_logger (logger)
644 : {
645 : }
646 :
647 899 : void add (region_offset offset, enum kind k)
648 : {
649 323 : m_all_offsets.insert (offset);
650 576 : if (k == kind::HARD)
651 398 : m_hard_offsets.insert (offset);
652 : }
653 :
654 288 : void add (const access_range &range, enum kind kind)
655 : {
656 288 : add (range.m_start, kind);
657 288 : add (range.m_next, kind);
658 288 : if (m_logger)
659 : {
660 0 : m_logger->start_log_line ();
661 0 : m_logger->log_partial ("added access_range: ");
662 0 : range.dump_to_pp (m_logger->get_printer (), true);
663 0 : m_logger->log_partial (" (%s)",
664 : (kind == boundaries::kind::HARD)
665 : ? "HARD" : "soft");
666 0 : m_logger->end_log_line ();
667 : }
668 288 : }
669 :
670 76 : void add (const region ®, region_model_manager *mgr, enum kind kind)
671 : {
672 76 : add (access_range (reg.get_offset (mgr),
673 : reg.get_next_offset (mgr),
674 76 : *mgr),
675 : kind);
676 76 : }
677 :
678 : void add (const byte_range bytes, enum kind kind)
679 : {
680 : add (access_range (&m_base_reg, bytes), kind);
681 : }
682 :
683 42 : void add_all_bytes_in_range (const byte_range &bytes)
684 : {
685 42 : for (byte_offset_t byte_idx = bytes.get_start_byte_offset ();
686 365 : byte_idx <= bytes.get_next_byte_offset ();
687 323 : byte_idx = byte_idx + 1)
688 323 : add (region_offset::make_concrete (&m_base_reg, byte_idx * 8),
689 : kind::SOFT);
690 42 : }
691 :
692 18 : void add_all_bytes_in_range (const access_range &range)
693 : {
694 18 : byte_range bytes (0, 0);
695 18 : bool valid = range.as_concrete_byte_range (&bytes);
696 18 : gcc_assert (valid);
697 18 : add_all_bytes_in_range (bytes);
698 18 : }
699 :
700 0 : void log (logger &logger) const
701 : {
702 0 : logger.log ("boundaries:");
703 0 : logger.inc_indent ();
704 0 : for (auto offset : m_all_offsets)
705 : {
706 0 : enum kind k = get_kind (offset);
707 0 : logger.start_log_line ();
708 0 : logger.log_partial ("%s: ", (k == kind::HARD) ? "HARD" : "soft");
709 0 : offset.dump_to_pp (logger.get_printer (), true);
710 0 : logger.end_log_line ();
711 : }
712 0 : logger.dec_indent ();
713 0 : }
714 :
715 391 : enum kind get_kind (region_offset offset) const
716 : {
717 391 : gcc_assert (m_all_offsets.find (offset) != m_all_offsets.end ());
718 391 : if (m_hard_offsets.find (offset) != m_hard_offsets.end ())
719 : return kind::HARD;
720 : else
721 286 : return kind::SOFT;
722 : }
723 :
724 72 : std::set<region_offset>::const_iterator begin () const
725 : {
726 72 : return m_all_offsets.begin ();
727 : }
728 72 : std::set<region_offset>::const_iterator end () const
729 : {
730 72 : return m_all_offsets.end ();
731 : }
732 : std::set<region_offset>::size_type size () const
733 : {
734 : return m_all_offsets.size ();
735 : }
736 :
737 : std::vector<region_offset>
738 21 : get_hard_boundaries_in_range (byte_offset_t min_offset,
739 : byte_offset_t max_offset) const
740 : {
741 21 : std::vector<region_offset> result;
742 99 : for (auto &offset : m_hard_offsets)
743 : {
744 78 : if (!offset.concrete_p ())
745 46 : continue;
746 78 : byte_offset_t byte;
747 78 : if (!offset.get_concrete_byte_offset (&byte))
748 0 : continue;
749 78 : if (byte < min_offset)
750 4 : continue;
751 74 : if (byte > max_offset)
752 42 : continue;
753 32 : result.push_back (offset);
754 : }
755 21 : return result;
756 : }
757 :
758 : private:
759 : const region &m_base_reg;
760 : logger *m_logger;
761 : std::set<region_offset> m_all_offsets;
762 : std::set<region_offset> m_hard_offsets;
763 : };
764 :
765 : /* A widget that wraps a table but offloads column-width calculation
766 : to a shared object, so that we can vertically line up multiple tables
767 : and have them all align their columns.
768 :
769 : For example, in:
770 :
771 : 01| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
772 : 02| |[0]|[1]|[2]|[3]|[4]|[5]| ... |[440]|[441]|[442]|[443]|[444]|[445]|
773 : 03| +---+---+---+---+---+---+ +-----+-----+-----+-----+-----+-----+
774 : 04| |'L'|'o'|'r'|'e'|'m'|' '| | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
775 : 05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
776 : 06| | string literal (type: 'char[446]') |
777 : 07| +----------------------------------------------------------------------+
778 : 08| | | | | | | | | | | | | | | |
779 : 09| | | | | | | | | | | | | | | |
780 : 10| v v v v v v v v v v v v v v v
781 : 11|+---+---------------------+----++--------------------------------------+
782 : 12||[0]| ... |[99]|| after valid range |
783 : 13|+---+---------------------+----+| |
784 : 14|| 'buf' (type: 'char[100]') || |
785 : 15|+------------------------------++--------------------------------------+
786 : 16||~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
787 : 17| | |
788 : 18| +---------+---------+ +----------+----------+
789 : 19| |capacity: 100 bytes| |overflow of 346 bytes|
790 : 20| +-------------------+ +---------------------+
791 :
792 : rows 01-07 and rows 11-15 are x_aligned_table_widget instances. */
793 :
794 : class x_aligned_table_widget : public leaf_widget
795 : {
796 : public:
797 137 : x_aligned_table_widget (table t,
798 : const theme &theme,
799 : table_dimension_sizes &col_widths)
800 137 : : m_table (std::move (t)),
801 137 : m_theme (theme),
802 137 : m_col_widths (col_widths),
803 137 : m_row_heights (t.get_size ().h),
804 137 : m_cell_sizes (m_col_widths, m_row_heights),
805 274 : m_tg (m_table, m_cell_sizes)
806 : {
807 137 : }
808 :
809 0 : const char *get_desc () const override
810 : {
811 0 : return "x_aligned_table_widget";
812 : }
813 :
814 137 : canvas::size_t calc_req_size () final override
815 : {
816 : /* We don't compute the size requirements;
817 : the parent should have done this. */
818 137 : return m_tg.get_canvas_size ();
819 : }
820 :
821 137 : void paint_to_canvas (canvas &canvas) final override
822 : {
823 137 : m_table.paint_to_canvas (canvas,
824 137 : get_top_left (),
825 137 : m_tg,
826 : m_theme);
827 137 : }
828 :
829 274 : const table &get_table () const { return m_table; }
830 274 : table_cell_sizes &get_cell_sizes () { return m_cell_sizes; }
831 137 : void recalc_coords ()
832 : {
833 137 : m_tg.recalc_coords ();
834 : }
835 :
836 : private:
837 : table m_table;
838 : const theme &m_theme;
839 : table_dimension_sizes &m_col_widths; // Reference to shared column widths
840 : table_dimension_sizes m_row_heights; // Unique row heights
841 : table_cell_sizes m_cell_sizes;
842 : table_geometry m_tg;
843 : };
844 :
845 : /* A widget for printing arrows between the accessed region
846 : and the svalue, showing the direction of the access.
847 :
848 : For example, in:
849 :
850 : 01| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
851 : 02| |[0]|[1]|[2]|[3]|[4]|[5]| ... |[440]|[441]|[442]|[443]|[444]|[445]|
852 : 03| +---+---+---+---+---+---+ +-----+-----+-----+-----+-----+-----+
853 : 04| |'L'|'o'|'r'|'e'|'m'|' '| | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
854 : 05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
855 : 06| | string literal (type: 'char[446]') |
856 : 07| +----------------------------------------------------------------------+
857 : 08| | | | | | | | | | | | | | | |
858 : 09| | | | | | | | | | | | | | | |
859 : 10| v v v v v v v v v v v v v v v
860 : 11|+---+---------------------+----++--------------------------------------+
861 : 12||[0]| ... |[99]|| after valid range |
862 : 13|+---+---------------------+----+| |
863 : 14|| 'buf' (type: 'char[100]') || |
864 : 15|+------------------------------++--------------------------------------+
865 : 16||~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
866 : 17| | |
867 : 18| +---------+---------+ +----------+----------+
868 : 19| |capacity: 100 bytes| |overflow of 346 bytes|
869 : 20| +-------------------+ +---------------------+
870 :
871 : rows 8-10 are the direction widget. */
872 :
873 : class direction_widget : public leaf_widget
874 : {
875 : public:
876 68 : direction_widget (const access_diagram_impl &dia_impl,
877 : const bit_to_table_map &btm)
878 68 : : leaf_widget (),
879 68 : m_dia_impl (dia_impl),
880 68 : m_btm (btm)
881 : {
882 : }
883 0 : const char *get_desc () const override
884 : {
885 0 : return "direction_widget";
886 : }
887 68 : canvas::size_t calc_req_size () final override
888 : {
889 : /* Get our width from our siblings. */
890 68 : return canvas::size_t (0, 3);
891 : }
892 : void paint_to_canvas (canvas &canvas) final override;
893 :
894 : private:
895 : const access_diagram_impl &m_dia_impl;
896 : const bit_to_table_map &m_btm;
897 : };
898 :
899 : /* A widget for adding an x_ruler to a diagram based on table columns,
900 : offloading column-width calculation to shared objects, so that the ruler
901 : lines up with other tables in the diagram.
902 :
903 : For example, in:
904 :
905 : 01| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
906 : 02| |[0]|[1]|[2]|[3]|[4]|[5]| ... |[440]|[441]|[442]|[443]|[444]|[445]|
907 : 03| +---+---+---+---+---+---+ +-----+-----+-----+-----+-----+-----+
908 : 04| |'L'|'o'|'r'|'e'|'m'|' '| | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
909 : 05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
910 : 06| | string literal (type: 'char[446]') |
911 : 07| +----------------------------------------------------------------------+
912 : 08| | | | | | | | | | | | | | | |
913 : 09| | | | | | | | | | | | | | | |
914 : 10| v v v v v v v v v v v v v v v
915 : 11|+---+---------------------+----++--------------------------------------+
916 : 12||[0]| ... |[99]|| after valid range |
917 : 13|+---+---------------------+----+| |
918 : 14|| 'buf' (type: 'char[100]') || |
919 : 15|+------------------------------++--------------------------------------+
920 : 16||~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
921 : 17| | |
922 : 18| +---------+---------+ +----------+----------+
923 : 19| |capacity: 100 bytes| |overflow of 346 bytes|
924 : 20| +-------------------+ +---------------------+
925 :
926 : rows 16-20 are the x_aligned_x_ruler_widget. */
927 :
928 : class x_aligned_x_ruler_widget : public leaf_widget
929 : {
930 : public:
931 68 : x_aligned_x_ruler_widget (const access_diagram_impl &dia_impl,
932 : const theme &theme)
933 68 : : m_dia_impl (dia_impl),
934 68 : m_theme (theme)
935 : {
936 : }
937 :
938 0 : const char *get_desc () const override
939 : {
940 0 : return "x_aligned_ruler_widget";
941 : }
942 :
943 153 : void add_range (const table::range_t &x_range,
944 : styled_string text,
945 : style::id_t style_id)
946 : {
947 153 : m_labels.push_back (label (x_range, std::move (text), style_id));
948 153 : }
949 :
950 68 : canvas::size_t calc_req_size () final override
951 : {
952 68 : x_ruler r (make_x_ruler ());
953 68 : return r.get_size ();
954 68 : }
955 :
956 68 : void paint_to_canvas (canvas &canvas) final override
957 : {
958 68 : x_ruler r (make_x_ruler ());
959 68 : r.paint_to_canvas (canvas,
960 68 : get_top_left (),
961 : m_theme);
962 68 : }
963 :
964 : private:
965 562 : struct label
966 : {
967 153 : label (const table::range_t &table_x_range,
968 : styled_string text,
969 : style::id_t style_id)
970 153 : : m_table_x_range (table_x_range),
971 153 : m_text (std::move (text)),
972 153 : m_style_id (style_id)
973 : {
974 : }
975 : table::range_t m_table_x_range;
976 : styled_string m_text;
977 : style::id_t m_style_id;
978 : };
979 :
980 : x_ruler make_x_ruler () const;
981 :
982 : const access_diagram_impl &m_dia_impl;
983 : const theme &m_theme;
984 : std::vector<label> m_labels;
985 : };
986 :
987 : /* A two-way mapping between access_ranges and table columns, for use by
988 : spatial_item subclasses for creating tables.
989 : For example when visualizing a bogus access of 'int arr[10];'
990 : at 'arr[10]', we might have:
991 : - table column 0 is "bytes 0-3" (for arr[0])
992 : - table column 1 is "bytes 4-35" (for arr[1] through arr[8])
993 : - table column 2 is "bytes 36-39 (for arr[9])
994 : - table column 3 is blank to emphasize a hard boundary between
995 : valid/invalid accesses.
996 : - table column 4 is "bytes 40-44" (for arr[10])
997 :
998 : We store this as a pair of maps from region_offset to table x; in
999 : the abvove example:
1000 :
1001 : region offset table_x prev_table_x
1002 : bit 0 (aka byte 0) 0 (none)
1003 : bit 32 (aka byte 4) 1 0
1004 : bit 288 (aka byte 36) 2 1
1005 : bit 320 (aka byte 40) 4 2
1006 : bit 352 (aka byte 44) (none) (none)
1007 :
1008 : so that e.g given the half-open byte range [0, 40)
1009 : we can determine the closed range of table x [0, 2]. */
1010 :
1011 72 : class bit_to_table_map
1012 : {
1013 : public:
1014 : /* Populate m_table_x_for_bit and m_bit_for_table_x. */
1015 72 : void populate (const boundaries &boundaries,
1016 : region_model_manager &mgr,
1017 : logger *logger)
1018 : {
1019 72 : LOG_SCOPE (logger);
1020 :
1021 72 : int table_x = 0;
1022 72 : std::vector <region_offset> vec_boundaries (boundaries.begin (),
1023 72 : boundaries.end ());
1024 :
1025 : /* Sort into an order that makes sense. */
1026 72 : std::sort (vec_boundaries.begin (),
1027 : vec_boundaries.end ());
1028 :
1029 72 : if (logger)
1030 : {
1031 0 : logger->log ("vec_boundaries");
1032 0 : logger->inc_indent ();
1033 0 : for (unsigned idx = 0; idx < vec_boundaries.size (); idx++)
1034 : {
1035 0 : logger->start_log_line ();
1036 0 : logger->log_partial ("idx: %i: ", idx);
1037 0 : vec_boundaries[idx].dump_to_pp (logger->get_printer (), true);
1038 0 : logger->end_log_line ();
1039 : }
1040 0 : logger->dec_indent ();
1041 : }
1042 :
1043 607 : for (size_t idx = 0; idx < vec_boundaries.size (); idx++)
1044 : {
1045 535 : const region_offset &offset = vec_boundaries[idx];
1046 535 : if (idx > 0 && (idx + 1) < vec_boundaries.size ())
1047 : {
1048 391 : if (boundaries.get_kind (offset) == boundaries::kind::HARD)
1049 105 : table_x += 1;
1050 : }
1051 535 : m_table_x_for_offset[offset] = table_x;
1052 535 : if ((idx + 1) < vec_boundaries.size ())
1053 : {
1054 463 : const region_offset &next_offset = vec_boundaries[idx + 1];
1055 463 : m_table_x_for_prev_offset[next_offset] = table_x;
1056 463 : m_range_for_table_x[table_x]
1057 926 : = access_range (offset, next_offset, mgr);
1058 : }
1059 535 : table_x += 1;
1060 : }
1061 72 : m_num_columns = table_x - 1;
1062 :
1063 72 : if (logger)
1064 0 : log (*logger);
1065 72 : }
1066 :
1067 1714 : unsigned get_num_columns () const
1068 : {
1069 1714 : return m_num_columns;
1070 : }
1071 :
1072 1103 : table::range_t get_table_x_for_range (const access_range &range) const
1073 : {
1074 1103 : return table::range_t (get_table_x_for_offset (range.m_start),
1075 1103 : get_table_x_for_prev_offset (range.m_next) + 1);
1076 : }
1077 :
1078 734 : table::rect_t get_table_rect (const access_range &range,
1079 : const int table_y, const int table_h) const
1080 : {
1081 734 : const table::range_t x_range (get_table_x_for_range (range));
1082 734 : return table::rect_t (table::coord_t (x_range.start, table_y),
1083 734 : table::size_t (x_range.get_size (), table_h));
1084 : }
1085 :
1086 543 : table::rect_t get_table_rect (const region *base_reg,
1087 : const bit_range &bits,
1088 : const int table_y, const int table_h) const
1089 : {
1090 543 : const access_range range (base_reg, bits);
1091 543 : return get_table_rect (range, table_y, table_h);
1092 : }
1093 :
1094 543 : table::rect_t get_table_rect (const region *base_reg,
1095 : const byte_range &bytes,
1096 : const int table_y, const int table_h) const
1097 : {
1098 543 : return get_table_rect (base_reg, bytes.as_bit_range (), table_y, table_h);
1099 : }
1100 :
1101 1117 : bool maybe_get_access_range_for_table_x (int table_x,
1102 : access_range *out) const
1103 : {
1104 1117 : auto slot = m_range_for_table_x.find (table_x);
1105 1117 : if (slot == m_range_for_table_x.end ())
1106 : return false;
1107 914 : *out = slot->second;
1108 914 : return true;
1109 : }
1110 :
1111 0 : void log (logger &logger) const
1112 : {
1113 0 : logger.log ("table columns");
1114 0 : logger.inc_indent ();
1115 0 : for (unsigned table_x = 0; table_x < get_num_columns (); table_x++)
1116 : {
1117 0 : logger.start_log_line ();
1118 0 : logger.log_partial ("table_x: %i", table_x);
1119 0 : access_range range_for_column (nullptr, bit_range (0, 0));
1120 0 : if (maybe_get_access_range_for_table_x (table_x, &range_for_column))
1121 : {
1122 0 : logger.log_partial (": range: ");
1123 0 : range_for_column.dump_to_pp (logger.get_printer (), true);
1124 : }
1125 0 : logger.end_log_line ();
1126 : }
1127 0 : logger.dec_indent ();
1128 0 : }
1129 :
1130 1135 : int get_table_x_for_offset (region_offset offset) const
1131 : {
1132 1135 : auto slot = m_table_x_for_offset.find (offset);
1133 :
1134 : /* If this fails, then we probably failed to fully populate m_boundaries
1135 : in find_boundaries. */
1136 1135 : gcc_assert (slot != m_table_x_for_offset.end ());
1137 :
1138 1135 : return slot->second;
1139 : }
1140 :
1141 : private:
1142 1103 : int get_table_x_for_prev_offset (region_offset offset) const
1143 : {
1144 1103 : auto slot = m_table_x_for_prev_offset.find (offset);
1145 :
1146 : /* If this fails, then we probably failed to fully populate m_boundaries
1147 : in find_boundaries. */
1148 1103 : gcc_assert (slot != m_table_x_for_prev_offset.end ());
1149 :
1150 1103 : return slot->second;
1151 : }
1152 :
1153 : std::map<region_offset, int> m_table_x_for_offset;
1154 : std::map<region_offset, int> m_table_x_for_prev_offset;
1155 : std::map<int, access_range> m_range_for_table_x;
1156 : unsigned m_num_columns;
1157 : };
1158 :
1159 : /* Base class for something in the diagram that participates
1160 : in two steps of diagram creation:
1161 : (a) populating a boundaries instance with the boundaries of interest
1162 : (b) creating a table instance for itself.
1163 :
1164 : Offsets in the boundaries are all expressed relative to the base
1165 : region of the access_operation. */
1166 :
1167 223 : class spatial_item
1168 : {
1169 : public:
1170 : virtual ~spatial_item () {}
1171 : virtual void add_boundaries (boundaries &out, logger *) const = 0;
1172 :
1173 : virtual table make_table (const bit_to_table_map &btm,
1174 : style_manager &sm) const = 0;
1175 : };
1176 :
1177 : /* A spatial_item that involves showing an svalue at a particular offset. */
1178 :
1179 : class svalue_spatial_item : public spatial_item
1180 : {
1181 : public:
1182 : enum class kind
1183 : {
1184 : WRITTEN,
1185 : EXISTING
1186 : };
1187 : protected:
1188 41 : svalue_spatial_item (const svalue &sval,
1189 : access_range bits,
1190 : enum kind kind)
1191 41 : : m_sval (sval), m_bits (bits), m_kind (kind)
1192 : {
1193 : }
1194 :
1195 : const svalue &m_sval;
1196 : access_range m_bits;
1197 : enum kind m_kind;
1198 : };
1199 :
1200 : static std::unique_ptr<spatial_item>
1201 : make_existing_svalue_spatial_item (const svalue *sval,
1202 : const access_range &bits,
1203 : const theme &theme);
1204 :
1205 : class compound_svalue_spatial_item : public svalue_spatial_item
1206 : {
1207 : public:
1208 11 : compound_svalue_spatial_item (const compound_svalue &sval,
1209 : const access_range &bits,
1210 : enum kind kind,
1211 : const theme &theme)
1212 11 : : svalue_spatial_item (sval, bits, kind),
1213 11 : m_compound_sval (sval)
1214 : {
1215 11 : const binding_map &map = m_compound_sval.get_map ();
1216 11 : auto_vec <const binding_key *> binding_keys;
1217 37 : for (auto iter : map)
1218 : {
1219 26 : const binding_key *key = iter.m_key;
1220 26 : const svalue *bound_sval = iter.m_sval;
1221 52 : if (const concrete_binding *concrete_key
1222 26 : = key->dyn_cast_concrete_binding ())
1223 : {
1224 26 : access_range range (nullptr,
1225 26 : concrete_key->get_bit_range ());
1226 26 : if (std::unique_ptr<spatial_item> child
1227 : = make_existing_svalue_spatial_item (bound_sval,
1228 : range,
1229 26 : theme))
1230 26 : m_children.push_back (std::move (child));
1231 : }
1232 : }
1233 11 : }
1234 :
1235 11 : void add_boundaries (boundaries &out, logger *logger) const final override
1236 : {
1237 11 : LOG_SCOPE (logger);
1238 22 : for (auto &iter : m_children)
1239 11 : iter->add_boundaries (out, logger);
1240 11 : }
1241 :
1242 11 : table make_table (const bit_to_table_map &btm,
1243 : style_manager &sm) const final override
1244 : {
1245 11 : std::vector<table> child_tables;
1246 11 : int max_rows = 0;
1247 22 : for (auto &iter : m_children)
1248 : {
1249 11 : table child_table (iter->make_table (btm, sm));
1250 11 : max_rows = MAX (max_rows, child_table.get_size ().h);
1251 11 : child_tables.push_back (std::move (child_table));
1252 11 : }
1253 11 : table t (table::size_t (btm.get_num_columns (), max_rows));
1254 22 : for (auto &&child_table : child_tables)
1255 11 : t.add_other_table (std::move (child_table),
1256 11 : table::coord_t (0, 0));
1257 22 : return t;
1258 11 : }
1259 :
1260 : private:
1261 : const compound_svalue &m_compound_sval;
1262 : std::vector<std::unique_ptr<spatial_item>> m_children;
1263 : };
1264 :
1265 : /* Loop through the TABLE_X_RANGE columns of T, adding
1266 : cells containing "..." in any unoccupied ranges of table cell. */
1267 :
1268 : static void
1269 38 : add_ellipsis_to_gaps (table &t,
1270 : style_manager &sm,
1271 : const table::range_t &table_x_range,
1272 : const table::range_t &table_y_range)
1273 : {
1274 38 : int table_x = table_x_range.get_min ();
1275 80 : while (table_x < table_x_range.get_next ())
1276 : {
1277 : /* Find a run of unoccupied table cells. */
1278 : const int start_table_x = table_x;
1279 236 : while (table_x < table_x_range.get_next ()
1280 236 : && !t.get_placement_at (table::coord_t (table_x,
1281 205 : table_y_range.get_min ())))
1282 194 : table_x++;
1283 42 : const table::range_t unoccupied_x_range (start_table_x, table_x);
1284 42 : if (unoccupied_x_range.get_size () > 0)
1285 42 : t.set_cell_span (table::rect_t (unoccupied_x_range, table_y_range),
1286 84 : styled_string (sm, "..."));
1287 : /* Skip occupied table cells. */
1288 53 : while (table_x < table_x_range.get_next ()
1289 53 : && t.get_placement_at (table::coord_t (table_x,
1290 21 : table_y_range.get_min ())))
1291 11 : table_x++;
1292 : }
1293 38 : }
1294 :
1295 : /* Subclass of spatial_item for visualizing the region of memory
1296 : that's valid to access relative to the base region of region accessed in
1297 : the operation. */
1298 :
1299 : class valid_region_spatial_item : public spatial_item
1300 : {
1301 : public:
1302 72 : valid_region_spatial_item (const access_operation &op,
1303 : diagnostics::paths::event_id_t region_creation_event_id,
1304 : const theme &theme)
1305 72 : : m_op (op),
1306 72 : m_region_creation_event_id (region_creation_event_id),
1307 72 : m_boundaries (nullptr),
1308 144 : m_existing_sval (op.m_model.get_store_value (op.m_base_region, nullptr)),
1309 72 : m_existing_sval_spatial_item
1310 : (make_existing_svalue_spatial_item (m_existing_sval,
1311 72 : op.get_valid_bits (),
1312 72 : theme))
1313 : {
1314 72 : }
1315 :
1316 72 : void add_boundaries (boundaries &out, logger *logger) const final override
1317 : {
1318 72 : LOG_SCOPE (logger);
1319 72 : m_boundaries = &out;
1320 72 : access_range valid_bits = m_op.get_valid_bits ();
1321 72 : if (logger)
1322 : {
1323 0 : logger->start_log_line ();
1324 0 : logger->log_partial ("valid bits: ");
1325 0 : valid_bits.dump_to_pp (logger->get_printer (), true);
1326 0 : logger->end_log_line ();
1327 : }
1328 72 : out.add (valid_bits, boundaries::kind::HARD);
1329 :
1330 72 : if (m_existing_sval_spatial_item)
1331 : {
1332 13 : if (logger)
1333 : {
1334 0 : logger->start_log_line ();
1335 0 : logger->log_partial ("existing svalue: ");
1336 0 : m_existing_sval->dump_to_pp (logger->get_printer (), true);
1337 0 : logger->end_log_line ();
1338 : }
1339 13 : m_existing_sval_spatial_item->add_boundaries (out, logger);
1340 : }
1341 :
1342 : /* Support for showing first and final element in array types. */
1343 72 : if (tree base_type = m_op.m_base_region->get_type ())
1344 39 : if (TREE_CODE (base_type) == ARRAY_TYPE)
1345 : {
1346 39 : if (logger)
1347 0 : logger->log ("showing first and final element in array type");
1348 39 : region_model_manager *mgr = m_op.m_model.get_manager ();
1349 39 : tree domain = TYPE_DOMAIN (base_type);
1350 39 : if (domain && TYPE_MIN_VALUE (domain) && TYPE_MAX_VALUE (domain))
1351 : {
1352 38 : const svalue *min_idx_sval
1353 38 : = mgr->get_or_create_constant_svalue (TYPE_MIN_VALUE (domain));
1354 38 : const svalue *max_idx_sval
1355 38 : = mgr->get_or_create_constant_svalue (TYPE_MAX_VALUE (domain));
1356 38 : const region *min_element =
1357 38 : mgr->get_element_region (m_op.m_base_region,
1358 38 : TREE_TYPE (base_type),
1359 : min_idx_sval);
1360 38 : out.add (*min_element, mgr, boundaries::kind::SOFT);
1361 38 : const region *max_element =
1362 38 : mgr->get_element_region (m_op.m_base_region,
1363 38 : TREE_TYPE (base_type),
1364 : max_idx_sval);
1365 38 : out.add (*max_element, mgr, boundaries::kind::SOFT);
1366 : }
1367 : }
1368 72 : }
1369 :
1370 : /* Subroutine of make_table when base region has ARRAY_TYPE. */
1371 39 : void add_array_elements_to_table (table &t,
1372 : const bit_to_table_map &btm,
1373 : style_manager &sm) const
1374 : {
1375 39 : tree base_type = m_op.m_base_region->get_type ();
1376 39 : gcc_assert (TREE_CODE (base_type) == ARRAY_TYPE);
1377 39 : gcc_assert (m_boundaries != nullptr);
1378 :
1379 39 : tree domain = TYPE_DOMAIN (base_type);
1380 39 : if (!(domain && TYPE_MIN_VALUE (domain) && TYPE_MAX_VALUE (domain)))
1381 1 : return;
1382 :
1383 38 : const int table_y = 0;
1384 38 : const int table_h = 1;
1385 38 : const table::range_t table_y_range (table_y, table_y + table_h);
1386 :
1387 38 : t.add_row ();
1388 :
1389 38 : const table::range_t min_x_range
1390 38 : = maybe_add_array_index_to_table (t, btm, sm, table_y_range,
1391 38 : TYPE_MIN_VALUE (domain));
1392 38 : const table::range_t max_x_range
1393 38 : = maybe_add_array_index_to_table (t, btm, sm, table_y_range,
1394 38 : TYPE_MAX_VALUE (domain));
1395 :
1396 38 : if (TREE_TYPE (base_type) == char_type_node)
1397 : {
1398 : /* For a char array,: if there are any hard boundaries in
1399 : m_boundaries that are *within* the valid region,
1400 : then show those index values. */
1401 21 : std::vector<region_offset> hard_boundaries
1402 21 : = m_boundaries->get_hard_boundaries_in_range
1403 21 : (tree_to_shwi (TYPE_MIN_VALUE (domain)),
1404 42 : tree_to_shwi (TYPE_MAX_VALUE (domain)));
1405 53 : for (auto &offset : hard_boundaries)
1406 : {
1407 32 : const int table_x = btm.get_table_x_for_offset (offset);
1408 32 : if (!offset.concrete_p ())
1409 0 : continue;
1410 32 : byte_offset_t byte;
1411 32 : if (!offset.get_concrete_byte_offset (&byte))
1412 0 : continue;
1413 32 : table::range_t table_x_range (table_x, table_x + 1);
1414 64 : t.maybe_set_cell_span (table::rect_t (table_x_range,
1415 32 : table_y_range),
1416 64 : fmt_styled_string (sm, "[%wi]",
1417 : byte.to_shwi ()));
1418 : }
1419 21 : }
1420 :
1421 38 : add_ellipsis_to_gaps (t, sm,
1422 38 : table::range_t (min_x_range.get_next (),
1423 38 : max_x_range.get_min ()),
1424 : table_y_range);
1425 : }
1426 :
1427 : table::range_t
1428 76 : maybe_add_array_index_to_table (table &t,
1429 : const bit_to_table_map &btm,
1430 : style_manager &sm,
1431 : const table::range_t table_y_range,
1432 : tree idx_cst) const
1433 : {
1434 76 : region_model_manager * const mgr = m_op.get_manager ();
1435 76 : tree base_type = m_op.m_base_region->get_type ();
1436 76 : const svalue *idx_sval
1437 76 : = mgr->get_or_create_constant_svalue (idx_cst);
1438 76 : const region *element_reg = mgr->get_element_region (m_op.m_base_region,
1439 76 : TREE_TYPE (base_type),
1440 : idx_sval);
1441 76 : const access_range element_range (*element_reg, mgr);
1442 76 : const table::range_t element_x_range
1443 76 : = btm.get_table_x_for_range (element_range);
1444 :
1445 76 : t.maybe_set_cell_span (table::rect_t (element_x_range,
1446 76 : table_y_range),
1447 152 : fmt_styled_string (sm, "[%E]", idx_cst));
1448 :
1449 76 : return element_x_range;
1450 : }
1451 :
1452 68 : table make_table (const bit_to_table_map &btm,
1453 : style_manager &sm) const final override
1454 : {
1455 68 : table t (table::size_t (btm.get_num_columns (), 0));
1456 :
1457 68 : if (tree base_type = m_op.m_base_region->get_type ())
1458 39 : if (TREE_CODE (base_type) == ARRAY_TYPE)
1459 39 : add_array_elements_to_table (t, btm, sm);
1460 :
1461 : /* Make use of m_existing_sval_spatial_item, if any. */
1462 68 : if (m_existing_sval_spatial_item)
1463 : {
1464 13 : table table_for_existing
1465 13 : = m_existing_sval_spatial_item->make_table (btm, sm);
1466 13 : const int table_y = t.add_rows (table_for_existing.get_size ().h);
1467 13 : t.add_other_table (std::move (table_for_existing),
1468 13 : table::coord_t (0, table_y));
1469 13 : }
1470 :
1471 68 : access_range valid_bits = m_op.get_valid_bits ();
1472 68 : const int table_y = t.add_row ();
1473 68 : const int table_h = 1;
1474 68 : table::rect_t rect = btm.get_table_rect (valid_bits, table_y, table_h);
1475 68 : styled_string s;
1476 68 : switch (m_op.m_base_region->get_kind ())
1477 : {
1478 0 : default:
1479 0 : s = styled_string (sm, _("region"));
1480 0 : break;
1481 37 : case RK_DECL:
1482 37 : {
1483 37 : const decl_region *decl_reg
1484 37 : = as_a <const decl_region *> (m_op.m_base_region);
1485 37 : tree decl = decl_reg->get_decl ();
1486 37 : s = fmt_styled_string (sm, "%qE (type: %qT)",
1487 : decl,
1488 74 : TREE_TYPE (decl));
1489 : }
1490 37 : break;
1491 17 : case RK_HEAP_ALLOCATED:
1492 17 : {
1493 17 : if (m_region_creation_event_id.known_p ())
1494 34 : s = fmt_styled_string (sm, _("buffer allocated on heap at %@"),
1495 17 : &m_region_creation_event_id);
1496 : else
1497 0 : s = styled_string (sm, _("heap-allocated buffer"));
1498 : }
1499 : break;
1500 12 : case RK_ALLOCA:
1501 12 : {
1502 12 : if (m_region_creation_event_id.known_p ())
1503 24 : s = fmt_styled_string (sm, _("buffer allocated on stack at %@"),
1504 12 : &m_region_creation_event_id);
1505 : else
1506 0 : s = styled_string (sm, _("stack-allocated buffer"));
1507 : }
1508 : break;
1509 2 : case RK_STRING:
1510 2 : {
1511 2 : const string_region *string_reg
1512 2 : = as_a <const string_region *> (m_op.m_base_region);
1513 2 : tree string_cst = string_reg->get_string_cst ();
1514 2 : s = fmt_styled_string (sm, _("string literal (type: %qT)"),
1515 4 : TREE_TYPE (string_cst));
1516 : }
1517 2 : break;
1518 : }
1519 68 : t.set_cell_span (rect, std::move (s));
1520 :
1521 68 : return t;
1522 68 : }
1523 :
1524 : private:
1525 : const access_operation &m_op;
1526 : diagnostics::paths::event_id_t m_region_creation_event_id;
1527 : mutable const boundaries *m_boundaries;
1528 : const svalue *m_existing_sval;
1529 : std::unique_ptr<spatial_item> m_existing_sval_spatial_item;
1530 : };
1531 :
1532 : /* Subclass of spatial_item for visualizing the region of memory
1533 : that's actually accessed by the read or write, for reads and
1534 : for write cases where we don't know the svalue written. */
1535 :
1536 : class accessed_region_spatial_item : public spatial_item
1537 : {
1538 : public:
1539 72 : accessed_region_spatial_item (const access_operation &op) : m_op (op) {}
1540 :
1541 72 : void add_boundaries (boundaries &out, logger *logger) const final override
1542 : {
1543 72 : LOG_SCOPE (logger);
1544 72 : access_range actual_bits = m_op.get_actual_bits ();
1545 72 : if (logger)
1546 : {
1547 0 : logger->start_log_line ();
1548 0 : logger->log_partial ("actual bits: ");
1549 0 : actual_bits.dump_to_pp (logger->get_printer (), true);
1550 0 : logger->end_log_line ();
1551 : }
1552 72 : out.add (actual_bits, boundaries::kind::HARD);
1553 72 : }
1554 :
1555 17 : table make_table (const bit_to_table_map &btm,
1556 : style_manager &sm) const final override
1557 : {
1558 17 : table t (table::size_t (btm.get_num_columns (), 1));
1559 :
1560 17 : access_range actual_bits = m_op.get_actual_bits ();
1561 17 : const int table_y = 0;
1562 17 : const int table_h = 1;
1563 17 : table::rect_t rect = btm.get_table_rect (actual_bits, table_y, table_h);
1564 17 : t.set_cell_span (rect, styled_string (get_label_string (sm)));
1565 :
1566 17 : return t;
1567 : }
1568 :
1569 : private:
1570 17 : styled_string get_label_string (style_manager &sm) const
1571 : {
1572 17 : const access_range accessed_bits (m_op.get_actual_bits ());
1573 17 : return get_access_size_str (sm,
1574 : m_op,
1575 : accessed_bits,
1576 17 : m_op.m_reg.get_type ());
1577 : }
1578 :
1579 : const access_operation &m_op;
1580 : };
1581 :
1582 : /* Subclass of spatial_item for when we know the svalue being written
1583 : to the accessed region.
1584 : Can be subclassed to give visualizations of specific kinds of svalue. */
1585 :
1586 : class written_svalue_spatial_item : public spatial_item
1587 : {
1588 : public:
1589 38 : written_svalue_spatial_item (const access_operation &op,
1590 : const svalue &sval,
1591 : access_range actual_bits)
1592 38 : : m_op (op), m_sval (sval), m_actual_bits (actual_bits)
1593 : {}
1594 :
1595 38 : void add_boundaries (boundaries &out, logger *logger) const override
1596 : {
1597 38 : LOG_SCOPE (logger);
1598 38 : out.add (m_actual_bits, boundaries::kind::HARD);
1599 38 : }
1600 :
1601 34 : table make_table (const bit_to_table_map &btm,
1602 : style_manager &sm) const override
1603 : {
1604 34 : table t (table::size_t (btm.get_num_columns (), 0));
1605 :
1606 34 : const int table_y = t.add_row ();
1607 34 : const int table_h = 1;
1608 34 : table::rect_t rect = btm.get_table_rect (m_actual_bits, table_y, table_h);
1609 34 : t.set_cell_span (rect, styled_string (get_label_string (sm)));
1610 34 : return t;
1611 : }
1612 :
1613 : protected:
1614 34 : styled_string get_label_string (style_manager &sm) const
1615 : {
1616 34 : tree rep_tree = m_op.m_model.get_representative_tree (&m_sval);
1617 34 : if (rep_tree)
1618 : {
1619 30 : if (TREE_CODE (rep_tree) == SSA_NAME)
1620 17 : if (tree var = SSA_NAME_VAR (rep_tree))
1621 30 : rep_tree = var;
1622 30 : switch (TREE_CODE (rep_tree))
1623 : {
1624 : default:
1625 : break;
1626 13 : case INTEGER_CST:
1627 26 : return fmt_styled_string (sm, _("write of %<(%T) %E%>"),
1628 13 : TREE_TYPE (rep_tree),
1629 13 : rep_tree);
1630 :
1631 13 : case PARM_DECL:
1632 13 : case VAR_DECL:
1633 26 : return fmt_styled_string (sm, _("write from %qE (type: %qT)"),
1634 : rep_tree,
1635 13 : TREE_TYPE (rep_tree));
1636 : break;
1637 : }
1638 : }
1639 :
1640 8 : const access_range accessed_bits (m_op.get_actual_bits ());
1641 8 : return get_access_size_str (sm,
1642 : m_op,
1643 : accessed_bits,
1644 8 : m_sval.get_type ());
1645 : }
1646 :
1647 : const access_operation &m_op;
1648 : const svalue &m_sval;
1649 : access_range m_actual_bits;
1650 : };
1651 :
1652 : /* Subclass of svalue_spatial_item for initial_svalue of a string_region
1653 : i.e. for string literals.
1654 :
1655 : There are three cases:
1656 : (a) for long strings, show just the head and tail of the string,
1657 : with an ellipsis:
1658 : +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
1659 : |[0]|[1]|[2]|[3]|[4]|[5]| |[440]|[441]|[442]|[443]|[444]|[445]|
1660 : +---+---+---+---+---+---+ ... +-----+-----+-----+-----+-----+-----+
1661 : |‘L’|‘o’|‘r’|‘e’|‘m’|‘ ’| | ‘o’ | ‘r’ | ‘u’ | ‘m’ | ‘.’ | NUL |
1662 : +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
1663 : | string literal (type: ‘char[446]’) |
1664 : +----------------------------------------------------------------------+
1665 : (b) For sufficiently short strings, show the full string:
1666 : +----------+---------+---------+---------+---------+ +-----------------+
1667 : | [0] | [1] | [2] | [3] | [4] | | [5] |
1668 : +----------+---------+---------+---------+---------+ +-----------------+
1669 : | ‘h’ | ‘e’ | ‘l’ | ‘l’ | ‘o’ | | NUL |
1670 : +----------+---------+---------+---------+---------+-+-----------------+
1671 : | string literal (type: ‘char[6]’) |
1672 : +----------------------------------------------------------------------+
1673 : (c) for non-ASCII strings that are short enough to show the full string,
1674 : show how unicode code points of the bytes decoded as UTF-8:
1675 : +-----+-----+-----+----+----++----+----+----+----+----+----+----+------+
1676 : | [0] | [1] | [2] |[3] |[4] ||[5] |[6] |[7] |[8] |[9] |[10]|[11]| [12] |
1677 : +-----+-----+-----+----+----++----+----+----+----+----+----+----+------+
1678 : |0xe6 |0x96 |0x87 |0xe5|0xad||0x97|0xe5|0x8c|0x96|0xe3|0x81|0x91| 0x00 |
1679 : +-----+-----+-----+----+----++----+----+----+----+----+----+----+------+
1680 : | U+6587 | U+5b57 | U+5316 | U+3051 |U+0000|
1681 : +-----------------+---------------+--------------+--------------+------+
1682 : | string literal (type: ‘char[13]’) |
1683 : +----------------------------------------------------------------------+
1684 : and show the characters themselves if unicode is supported and they are not
1685 : control characters:
1686 : ┌─────┬─────┬─────┬────┬────┐┌────┬────┬────┬────┬────┬────┬────┬──────┐
1687 : │ [0] │ [1] │ [2] │[3] │[4] ││[5] │[6] │[7] │[8] │[9] │[10]│[11]│ [12] │
1688 : ├─────┼─────┼─────┼────┼────┤├────┼────┼────┼────┼────┼────┼────┼──────┤
1689 : │0xe6 │0x96 │0x87 │0xe5│0xad││0x97│0xe5│0x8c│0x96│0xe3│0x81│0x91│ 0x00 │
1690 : ├─────┴─────┴─────┼────┴────┴┴────┼────┴────┴────┼────┴────┴────┼──────┤
1691 : │ U+6587 │ U+5b57 │ U+5316 │ U+3051 │U+0000│
1692 : ├─────────────────┼───────────────┼──────────────┼──────────────┼──────┤
1693 : │ 文 │ 字 │ 化 │ け │ NUL │
1694 : ├─────────────────┴───────────────┴──────────────┴──────────────┴──────┤
1695 : │ string literal (type: ‘char[13]’) │
1696 : └──────────────────────────────────────────────────────────────────────┘
1697 : */
1698 :
1699 : class string_literal_spatial_item : public svalue_spatial_item
1700 : {
1701 : public:
1702 30 : string_literal_spatial_item (const svalue &sval,
1703 : access_range actual_bits,
1704 : const string_region &string_reg,
1705 : const theme &theme,
1706 : enum kind kind)
1707 30 : : svalue_spatial_item (sval, actual_bits, kind),
1708 30 : m_string_reg (string_reg),
1709 30 : m_theme (theme),
1710 30 : m_ellipsis_threshold (param_analyzer_text_art_string_ellipsis_threshold),
1711 30 : m_ellipsis_head_len (param_analyzer_text_art_string_ellipsis_head_len),
1712 30 : m_ellipsis_tail_len (param_analyzer_text_art_string_ellipsis_tail_len),
1713 60 : m_show_full_string (calc_show_full_string ()),
1714 30 : m_show_utf8 (m_show_full_string && !pure_ascii_p ())
1715 : {
1716 30 : }
1717 :
1718 30 : void add_boundaries (boundaries &out, logger *logger) const override
1719 : {
1720 30 : LOG_SCOPE (logger);
1721 30 : out.add (m_bits, m_kind == svalue_spatial_item::kind::WRITTEN
1722 30 : ? boundaries::kind::HARD
1723 : : boundaries::kind::SOFT);
1724 :
1725 30 : tree string_cst = get_string_cst ();
1726 : /* TREE_STRING_LENGTH is sizeof, not strlen. */
1727 30 : if (m_show_full_string)
1728 18 : out.add_all_bytes_in_range (m_bits);
1729 : else
1730 : {
1731 12 : byte_range bytes (0, 0);
1732 12 : bool valid = m_bits.as_concrete_byte_range (&bytes);
1733 12 : gcc_assert (valid);
1734 12 : byte_range head_of_string (bytes.get_start_byte_offset (),
1735 12 : m_ellipsis_head_len);
1736 12 : out.add_all_bytes_in_range (head_of_string);
1737 12 : byte_range tail_of_string
1738 12 : ((bytes.get_start_byte_offset ()
1739 12 : + TREE_STRING_LENGTH (string_cst)
1740 12 : - m_ellipsis_tail_len),
1741 24 : m_ellipsis_tail_len);
1742 12 : out.add_all_bytes_in_range (tail_of_string);
1743 : /* Adding the above pair of ranges will also effectively add
1744 : the boundaries of the range of ellipsized chars, as they're
1745 : exactly in between head_of_string and tail_of_string. */
1746 : }
1747 30 : }
1748 :
1749 30 : table make_table (const bit_to_table_map &btm,
1750 : style_manager &sm) const override
1751 : {
1752 30 : table t (table::size_t (btm.get_num_columns (), 0));
1753 :
1754 30 : const int byte_idx_table_y = (m_kind == svalue_spatial_item::kind::WRITTEN
1755 30 : ? t.add_row ()
1756 : : -1);
1757 30 : const int byte_val_table_y = t.add_row ();
1758 :
1759 30 : byte_range bytes (0, 0);
1760 30 : bool valid = m_bits.as_concrete_byte_range (&bytes);
1761 30 : gcc_assert (valid);
1762 30 : tree string_cst = get_string_cst ();
1763 30 : if (m_show_full_string)
1764 : {
1765 18 : for (byte_offset_t byte_idx_within_cluster
1766 18 : = bytes.get_start_byte_offset ();
1767 155 : byte_idx_within_cluster < bytes.get_next_byte_offset ();
1768 137 : byte_idx_within_cluster = byte_idx_within_cluster + 1)
1769 137 : add_column_for_byte
1770 137 : (t, btm, sm, byte_idx_within_cluster,
1771 274 : byte_idx_within_cluster - bytes.get_start_byte_offset (),
1772 : byte_idx_table_y, byte_val_table_y);
1773 :
1774 18 : if (m_show_utf8)
1775 : {
1776 6 : const bool show_unichars = m_theme.unicode_p ();
1777 6 : const int utf8_code_point_table_y = t.add_row ();
1778 6 : int utf8_character_table_y;
1779 6 : if (show_unichars)
1780 5 : utf8_character_table_y = t.add_row ();
1781 :
1782 : /* We don't actually want the display widths here, but
1783 : it's an easy way to decode UTF-8. */
1784 6 : cpp_char_column_policy policy (8, cpp_wcwidth);
1785 6 : cpp_display_width_computation dw (TREE_STRING_POINTER (string_cst),
1786 6 : TREE_STRING_LENGTH (string_cst),
1787 6 : policy);
1788 47 : while (!dw.done ())
1789 : {
1790 35 : cpp_decoded_char decoded_char;
1791 35 : dw.process_next_codepoint (&decoded_char);
1792 :
1793 35 : if (!decoded_char.m_valid_ch)
1794 0 : continue;
1795 35 : size_t start_byte_idx
1796 35 : = decoded_char.m_start_byte - TREE_STRING_POINTER (string_cst);
1797 35 : byte_size_t size_in_bytes
1798 35 : = decoded_char.m_next_byte - decoded_char.m_start_byte;
1799 35 : byte_range cluster_bytes_for_codepoint
1800 35 : (start_byte_idx + bytes.get_start_byte_offset (),
1801 35 : size_in_bytes);
1802 :
1803 35 : const table::rect_t code_point_table_rect
1804 35 : = btm.get_table_rect (&m_string_reg,
1805 : cluster_bytes_for_codepoint,
1806 : utf8_code_point_table_y, 1);
1807 35 : char buf[100];
1808 35 : sprintf (buf, "U+%04x", decoded_char.m_ch);
1809 35 : t.set_cell_span (code_point_table_rect,
1810 70 : styled_string (sm, buf));
1811 :
1812 35 : if (show_unichars)
1813 : {
1814 30 : const table::rect_t character_table_rect
1815 30 : = btm.get_table_rect (&m_string_reg,
1816 : cluster_bytes_for_codepoint,
1817 : utf8_character_table_y, 1);
1818 30 : if (cpp_is_printable_char (decoded_char.m_ch))
1819 23 : t.set_cell_span (character_table_rect,
1820 46 : styled_string (decoded_char.m_ch));
1821 7 : else if (decoded_char.m_ch == 0)
1822 5 : t.set_cell_span (character_table_rect,
1823 10 : styled_string (sm, "NUL"));
1824 : else
1825 2 : t.set_cell_span (character_table_rect,
1826 4 : styled_string (sm, ""));
1827 : }
1828 : }
1829 : }
1830 : }
1831 : else
1832 : {
1833 : /* Head of string. */
1834 84 : for (int byte_idx = 0; byte_idx < m_ellipsis_head_len; byte_idx++)
1835 144 : add_column_for_byte (t, btm, sm,
1836 144 : byte_idx + bytes.get_start_byte_offset (),
1837 72 : byte_idx,
1838 : byte_idx_table_y, byte_val_table_y);
1839 :
1840 : /* Ellipsis. */
1841 12 : const byte_range ellipsis_bytes
1842 12 : (m_ellipsis_head_len + bytes.get_start_byte_offset (),
1843 12 : TREE_STRING_LENGTH (string_cst)
1844 12 : - (m_ellipsis_head_len + m_ellipsis_tail_len));
1845 12 : const table::rect_t table_rect
1846 : = ((byte_idx_table_y != -1)
1847 12 : ? btm.get_table_rect (&m_string_reg, ellipsis_bytes,
1848 : byte_idx_table_y, 2)
1849 6 : : btm.get_table_rect (&m_string_reg, ellipsis_bytes,
1850 : byte_val_table_y, 1));
1851 12 : t.set_cell_span(table_rect, styled_string (sm, "..."));
1852 :
1853 : /* Tail of string. */
1854 12 : for (int byte_idx
1855 12 : = (TREE_STRING_LENGTH (string_cst) - m_ellipsis_tail_len);
1856 84 : byte_idx < TREE_STRING_LENGTH (string_cst);
1857 : byte_idx++)
1858 144 : add_column_for_byte (t, btm, sm,
1859 144 : byte_idx + bytes.get_start_byte_offset (),
1860 72 : byte_idx,
1861 : byte_idx_table_y, byte_val_table_y);
1862 : }
1863 :
1864 30 : if (m_kind == svalue_spatial_item::kind::WRITTEN)
1865 : {
1866 17 : const int summary_table_y = t.add_row ();
1867 17 : t.set_cell_span (btm.get_table_rect (&m_string_reg, bytes,
1868 : summary_table_y, 1),
1869 34 : fmt_styled_string (sm,
1870 17 : _("string literal (type: %qT)"),
1871 17 : TREE_TYPE (string_cst)));
1872 : }
1873 :
1874 30 : return t;
1875 : }
1876 :
1877 60 : tree get_string_cst () const { return m_string_reg.get_string_cst (); }
1878 :
1879 : private:
1880 30 : bool calc_show_full_string () const
1881 : {
1882 30 : tree string_cst = get_string_cst ();
1883 30 : if (TREE_STRING_LENGTH (string_cst) < m_ellipsis_threshold)
1884 : return true;
1885 13 : if (TREE_STRING_LENGTH (string_cst) <
1886 13 : (m_ellipsis_head_len + m_ellipsis_tail_len))
1887 1 : return true;
1888 : return false;
1889 : }
1890 :
1891 18 : bool pure_ascii_p () const
1892 : {
1893 18 : tree string_cst = get_string_cst ();
1894 104 : for (unsigned byte_idx = 0;
1895 104 : byte_idx < (unsigned) TREE_STRING_LENGTH (string_cst);
1896 : byte_idx++)
1897 : {
1898 92 : unsigned char ch = TREE_STRING_POINTER (string_cst)[byte_idx];
1899 92 : if (ch >= 0x80)
1900 : return false;
1901 : }
1902 : return true;
1903 : }
1904 :
1905 281 : void add_column_for_byte (table &t, const bit_to_table_map &btm,
1906 : style_manager &sm,
1907 : const byte_offset_t byte_idx_within_cluster,
1908 : const byte_offset_t byte_idx_within_string,
1909 : const int byte_idx_table_y,
1910 : const int byte_val_table_y) const
1911 : {
1912 281 : tree string_cst = get_string_cst ();
1913 281 : gcc_assert (byte_idx_within_string >= 0);
1914 281 : gcc_assert (byte_idx_within_string < TREE_STRING_LENGTH (string_cst));
1915 :
1916 281 : const byte_range bytes (byte_idx_within_cluster, 1);
1917 281 : if (byte_idx_table_y != -1)
1918 : {
1919 168 : const table::rect_t idx_table_rect
1920 168 : = btm.get_table_rect (&m_string_reg, bytes, byte_idx_table_y, 1);
1921 336 : t.set_cell_span (idx_table_rect,
1922 336 : fmt_styled_string (sm, "[%wu]",
1923 : byte_idx_within_string.ulow ()));
1924 : }
1925 :
1926 281 : char byte_val
1927 281 : = TREE_STRING_POINTER (string_cst)[byte_idx_within_string.ulow ()];
1928 281 : const table::rect_t val_table_rect
1929 281 : = btm.get_table_rect (&m_string_reg, bytes, byte_val_table_y, 1);
1930 281 : table_cell_content content (make_cell_content_for_byte (sm, byte_val));
1931 281 : t.set_cell_span (val_table_rect, std::move (content));
1932 281 : }
1933 :
1934 281 : table_cell_content make_cell_content_for_byte (style_manager &sm,
1935 : unsigned char byte_val) const
1936 : {
1937 281 : if (!m_show_utf8)
1938 : {
1939 218 : if (byte_val == '\0')
1940 24 : return styled_string (sm, "NUL");
1941 194 : else if (byte_val < 0x80)
1942 194 : if (ISPRINT (byte_val))
1943 190 : return fmt_styled_string (sm, "%qc", byte_val);
1944 : }
1945 67 : char buf[100];
1946 67 : sprintf (buf, "0x%02x", byte_val);
1947 67 : return styled_string (sm, buf);
1948 : }
1949 :
1950 : const string_region &m_string_reg;
1951 : const theme &m_theme;
1952 : const int m_ellipsis_threshold;
1953 : const int m_ellipsis_head_len;
1954 : const int m_ellipsis_tail_len;
1955 : const bool m_show_full_string;
1956 : const bool m_show_utf8;
1957 : };
1958 :
1959 : static std::unique_ptr<spatial_item>
1960 55 : make_written_svalue_spatial_item (const access_operation &op,
1961 : const svalue &sval,
1962 : access_range actual_bits,
1963 : const theme &theme)
1964 : {
1965 55 : if (const initial_svalue *initial_sval = sval.dyn_cast_initial_svalue ())
1966 33 : if (const string_region *string_reg
1967 33 : = initial_sval->get_region ()->dyn_cast_string_region ())
1968 17 : return std::make_unique <string_literal_spatial_item>
1969 17 : (sval, actual_bits,
1970 : *string_reg, theme,
1971 17 : svalue_spatial_item::kind::WRITTEN);
1972 38 : return std::make_unique <written_svalue_spatial_item> (op, sval, actual_bits);
1973 : }
1974 :
1975 : static std::unique_ptr<spatial_item>
1976 98 : make_existing_svalue_spatial_item (const svalue *sval,
1977 : const access_range &bits,
1978 : const theme &theme)
1979 : {
1980 98 : if (!sval)
1981 0 : return nullptr;
1982 :
1983 98 : switch (sval->get_kind ())
1984 : {
1985 60 : default:
1986 60 : return nullptr;
1987 :
1988 27 : case SK_INITIAL:
1989 27 : {
1990 27 : const initial_svalue *initial_sval = (const initial_svalue *)sval;
1991 27 : if (const string_region *string_reg
1992 27 : = initial_sval->get_region ()->dyn_cast_string_region ())
1993 13 : return std::make_unique <string_literal_spatial_item>
1994 13 : (*sval, bits,
1995 : *string_reg, theme,
1996 13 : svalue_spatial_item::kind::EXISTING);
1997 14 : return nullptr;
1998 : }
1999 :
2000 11 : case SK_COMPOUND:
2001 11 : return std::make_unique<compound_svalue_spatial_item>
2002 11 : (*((const compound_svalue *)sval),
2003 : bits,
2004 22 : svalue_spatial_item::kind::EXISTING,
2005 11 : theme);
2006 : }
2007 : }
2008 :
2009 : /* Widget subclass implementing access diagrams. */
2010 :
2011 : class access_diagram_impl : public vbox_widget
2012 : {
2013 : public:
2014 72 : access_diagram_impl (const access_operation &op,
2015 : diagnostics::paths::event_id_t region_creation_event_id,
2016 : style_manager &sm,
2017 : const theme &theme,
2018 : logger *logger)
2019 72 : : m_op (op),
2020 72 : m_region_creation_event_id (region_creation_event_id),
2021 72 : m_sm (sm),
2022 72 : m_theme (theme),
2023 72 : m_logger (logger),
2024 72 : m_invalid (false),
2025 72 : m_valid_region_spatial_item (op, region_creation_event_id, theme),
2026 72 : m_accessed_region_spatial_item (op),
2027 72 : m_btm (),
2028 72 : m_calc_req_size_called (false)
2029 : {
2030 72 : LOG_SCOPE (logger);
2031 :
2032 72 : if (logger)
2033 : {
2034 0 : access_range invalid_before_bits;
2035 0 : if (op.maybe_get_invalid_before_bits (&invalid_before_bits))
2036 0 : invalid_before_bits.log ("invalid before range", *logger);
2037 0 : access_range invalid_after_bits;
2038 0 : if (op.maybe_get_invalid_after_bits (&invalid_after_bits))
2039 0 : invalid_after_bits.log ("invalid after range", *logger);
2040 :
2041 0 : if (op.m_sval_hint)
2042 : {
2043 0 : logger->start_log_line ();
2044 0 : logger->log_partial ("sval_hint: ");
2045 0 : op.m_sval_hint->dump_to_pp (logger->get_printer (), true);
2046 0 : logger->end_log_line ();
2047 : }
2048 : }
2049 :
2050 : /* Register painting styles. */
2051 72 : {
2052 72 : style valid_style (get_style_from_color_cap_name ("valid"));
2053 72 : m_valid_style_id = m_sm.get_or_create_id (valid_style);
2054 :
2055 72 : style invalid_style (get_style_from_color_cap_name ("invalid"));
2056 72 : m_invalid_style_id = m_sm.get_or_create_id (invalid_style);
2057 72 : }
2058 :
2059 72 : if (op.m_sval_hint)
2060 : {
2061 55 : access_range actual_bits = m_op.get_actual_bits ();
2062 55 : m_written_svalue_spatial_item
2063 55 : = make_written_svalue_spatial_item (m_op,
2064 55 : *op.m_sval_hint,
2065 : actual_bits,
2066 55 : m_theme);
2067 : }
2068 :
2069 : /* Two passes:
2070 : First, figure out all of the boundaries of interest.
2071 : Then use that to build child widgets showing the regions of interest,
2072 : with a common tabular layout. */
2073 :
2074 72 : m_boundaries = find_boundaries ();
2075 72 : if (logger)
2076 0 : m_boundaries->log (*logger);
2077 :
2078 : /* Populate m_table_x_for_bit and m_bit_for_table_x.
2079 : Each table column represents the range [offset, next_offset).
2080 : We don't create a column in the table for the final offset, but we
2081 : do populate it, so that looking at the table_x of one beyond the
2082 : final table column gives us the upper bound offset. */
2083 72 : m_btm.populate (*m_boundaries, *m_op.get_manager (), logger);
2084 :
2085 : /* Gracefully reject cases where the boundary sorting has gone wrong
2086 : (due to awkward combinations of symbolic values). */
2087 72 : {
2088 72 : table::range_t actual_bits_x_range
2089 72 : = m_btm.get_table_x_for_range (m_op.get_actual_bits ());
2090 72 : if (actual_bits_x_range.get_size () <= 0)
2091 : {
2092 4 : if (logger)
2093 0 : logger->log ("giving up: bad table columns for actual_bits");
2094 4 : m_invalid = true;
2095 4 : return;
2096 : }
2097 68 : table::range_t valid_bits_x_range
2098 68 : = m_btm.get_table_x_for_range (m_op.get_valid_bits ());
2099 68 : if (valid_bits_x_range.get_size () <= 0)
2100 : {
2101 0 : if (logger)
2102 0 : logger->log ("giving up: bad table columns for valid_bits");
2103 0 : m_invalid = true;
2104 0 : return;
2105 : }
2106 : }
2107 :
2108 68 : m_col_widths
2109 68 : = std::make_unique <table_dimension_sizes> (m_btm.get_num_columns ());
2110 :
2111 : /* Now create child widgets. */
2112 :
2113 68 : if (flag_analyzer_debug_text_art)
2114 : {
2115 1 : table t_headings (make_headings_table ());
2116 2 : add_aligned_child_table (std::move (t_headings));
2117 1 : }
2118 :
2119 68 : if (m_written_svalue_spatial_item)
2120 : {
2121 51 : table t_sval (m_written_svalue_spatial_item->make_table (m_btm, m_sm));
2122 102 : add_aligned_child_table (std::move (t_sval));
2123 51 : }
2124 : else
2125 : {
2126 17 : table t_accessed
2127 17 : (m_accessed_region_spatial_item.make_table (m_btm, m_sm));
2128 34 : add_aligned_child_table (std::move (t_accessed));
2129 17 : }
2130 :
2131 68 : add_direction_widget ();
2132 :
2133 68 : table t_valid (m_valid_region_spatial_item.make_table (m_btm, m_sm));
2134 68 : add_invalid_accesses_to_region_table (t_valid);
2135 68 : add_aligned_child_table (std::move (t_valid));
2136 :
2137 68 : add_valid_vs_invalid_ruler ();
2138 72 : }
2139 :
2140 0 : const char *get_desc () const override
2141 : {
2142 0 : return "access_diagram_impl";
2143 : }
2144 :
2145 72 : canvas::size_t calc_req_size () final override
2146 : {
2147 72 : if (m_invalid)
2148 4 : return canvas::size_t (0, 0);
2149 :
2150 : /* Now compute the size requirements for the tables. */
2151 205 : for (auto iter : m_aligned_table_widgets)
2152 137 : iter->get_cell_sizes ().pass_1 (iter->get_table ());
2153 205 : for (auto iter : m_aligned_table_widgets)
2154 137 : iter->get_cell_sizes ().pass_2 (iter->get_table ());
2155 :
2156 68 : adjust_to_scale();
2157 :
2158 : /* ...and relayout the tables. */
2159 205 : for (auto iter : m_aligned_table_widgets)
2160 137 : iter->recalc_coords ();
2161 :
2162 : /* Populate the canvas_x per table_x. */
2163 68 : m_col_start_x.clear ();
2164 68 : int iter_canvas_x = 0;
2165 624 : for (auto w : m_col_widths->m_requirements)
2166 : {
2167 556 : m_col_start_x.push_back (iter_canvas_x);
2168 556 : iter_canvas_x += w + 1;
2169 : }
2170 68 : m_col_start_x.push_back (iter_canvas_x);
2171 :
2172 68 : m_calc_req_size_called = true;
2173 :
2174 68 : return vbox_widget::calc_req_size ();
2175 : }
2176 :
2177 1172 : int get_canvas_x_for_table_x (int table_x) const
2178 : {
2179 1172 : gcc_assert (m_calc_req_size_called);
2180 1172 : return m_col_start_x[table_x];
2181 : }
2182 :
2183 586 : canvas::range_t get_canvas_x_range (const table::range_t &table_x_range) const
2184 : {
2185 586 : gcc_assert (m_calc_req_size_called);
2186 1172 : return canvas::range_t (get_canvas_x_for_table_x (table_x_range.start),
2187 586 : get_canvas_x_for_table_x (table_x_range.next));
2188 : }
2189 :
2190 416 : const access_operation &get_op () const { return m_op; }
2191 :
2192 280 : style::id_t get_style_id_for_validity (bool is_valid) const
2193 : {
2194 280 : return is_valid ? m_valid_style_id : m_invalid_style_id;
2195 : }
2196 :
2197 280 : const theme &get_theme () const { return m_theme; }
2198 :
2199 : private:
2200 : /* Figure out all of the boundaries of interest when visualizing ths op. */
2201 : std::unique_ptr<boundaries>
2202 72 : find_boundaries () const
2203 : {
2204 72 : auto result
2205 72 : = std::make_unique<boundaries> (*m_op.m_base_region, m_logger);
2206 :
2207 72 : m_valid_region_spatial_item.add_boundaries (*result, m_logger);
2208 72 : m_accessed_region_spatial_item.add_boundaries (*result, m_logger);
2209 72 : if (m_written_svalue_spatial_item)
2210 55 : m_written_svalue_spatial_item->add_boundaries (*result, m_logger);
2211 :
2212 72 : return result;
2213 : }
2214 :
2215 137 : void add_aligned_child_table (table t)
2216 : {
2217 137 : x_aligned_table_widget *w
2218 137 : = new x_aligned_table_widget (std::move (t), m_theme, *m_col_widths);
2219 137 : m_aligned_table_widgets.push_back (w);
2220 137 : add_child (std::unique_ptr<widget> (w));
2221 137 : }
2222 :
2223 : /* Create a table showing headings for use by -fanalyzer-debug-text-art, for
2224 : example:
2225 : +---------+-----------+-----------+---+--------------------------------+
2226 : | tc0 | tc1 | tc2 |tc3| tc4 |
2227 : +---------+-----------+-----------+---+--------------------------------+
2228 : |bytes 0-3|bytes 4-35 |bytes 36-39| | bytes 40-43 |
2229 : +---------+-----------+-----------+ +--------------------------------+
2230 : which has:
2231 : - a row showing the table column numbers, labelled "tc0", "tc1", etc
2232 : - a row showing the memory range of each table column that has one. */
2233 :
2234 1 : table make_headings_table () const
2235 : {
2236 1 : table t (table::size_t (m_btm.get_num_columns (), 2));
2237 :
2238 6 : for (int table_x = 0; table_x < t.get_size ().w; table_x++)
2239 : {
2240 5 : const int table_y = 0;
2241 5 : t.set_cell (table::coord_t (table_x, table_y),
2242 10 : fmt_styled_string (m_sm, "tc%i", table_x));
2243 : }
2244 6 : for (int table_x = 0; table_x < t.get_size ().w; table_x++)
2245 : {
2246 5 : const int table_y = 1;
2247 5 : access_range range_for_column (nullptr, bit_range (0, 0));
2248 5 : if (m_btm.maybe_get_access_range_for_table_x (table_x,
2249 : &range_for_column))
2250 : {
2251 4 : pretty_printer pp;
2252 4 : pp_format_decoder (&pp) = default_tree_printer;
2253 4 : range_for_column.dump_to_pp (&pp, true);
2254 4 : t.set_cell (table::coord_t (table_x, table_y),
2255 8 : styled_string (m_sm, pp_formatted_text (&pp)));
2256 4 : }
2257 : }
2258 :
2259 1 : return t;
2260 : }
2261 :
2262 68 : void add_direction_widget ()
2263 : {
2264 68 : add_child (std::make_unique<direction_widget> (*this, m_btm));
2265 68 : }
2266 :
2267 68 : void add_invalid_accesses_to_region_table (table &t_region)
2268 : {
2269 68 : gcc_assert (t_region.get_size ().w == (int)m_btm.get_num_columns ());
2270 :
2271 68 : const int table_y = 0;
2272 68 : const int table_h = t_region.get_size ().h;
2273 :
2274 68 : access_range invalid_before_bits;
2275 68 : if (m_op.maybe_get_invalid_before_bits (&invalid_before_bits))
2276 : {
2277 15 : t_region.set_cell_span (m_btm.get_table_rect (invalid_before_bits,
2278 : table_y, table_h),
2279 30 : styled_string (m_sm,
2280 15 : _("before valid range")));
2281 : }
2282 68 : access_range invalid_after_bits;
2283 68 : if (m_op.maybe_get_invalid_after_bits (&invalid_after_bits))
2284 : {
2285 57 : t_region.set_cell_span (m_btm.get_table_rect (invalid_after_bits,
2286 : table_y, table_h),
2287 114 : styled_string (m_sm,
2288 57 : _("after valid range")));
2289 : }
2290 68 : }
2291 :
2292 125 : void maybe_add_gap (x_aligned_x_ruler_widget *w,
2293 : const access_range &lower,
2294 : const access_range &upper) const
2295 : {
2296 125 : LOG_SCOPE (m_logger);
2297 125 : if (m_logger)
2298 : {
2299 0 : lower.log ("lower", *m_logger);
2300 0 : upper.log ("upper", *m_logger);
2301 : }
2302 125 : region_model_manager *mgr = m_op.get_manager ();
2303 125 : const svalue &lower_next = lower.m_next.calc_symbolic_bit_offset (mgr);
2304 125 : const svalue &upper_start = upper.m_start.calc_symbolic_bit_offset (mgr);
2305 125 : const svalue *num_bits_gap
2306 125 : = mgr->get_or_create_binop (NULL_TREE, MINUS_EXPR,
2307 : &upper_start, &lower_next);
2308 125 : if (m_logger)
2309 0 : m_logger->log ("num_bits_gap: %qs", num_bits_gap->get_desc ().get ());
2310 :
2311 125 : const svalue *zero = mgr->get_or_create_int_cst (NULL_TREE, 0);
2312 125 : tristate ts_gt_zero = m_op.m_model.eval_condition (num_bits_gap,
2313 : GT_EXPR,
2314 : zero);
2315 125 : if (ts_gt_zero.is_false ())
2316 : {
2317 111 : if (m_logger)
2318 0 : m_logger->log ("rejecting as not > 0");
2319 111 : return;
2320 : }
2321 :
2322 14 : bit_size_expr num_bits (*num_bits_gap);
2323 14 : if (auto p = num_bits.maybe_get_formatted_str (m_sm, m_op.m_model,
2324 14 : _("%wi bit"),
2325 14 : _("%wi bits"),
2326 14 : _("%wi byte"),
2327 14 : _("%wi bytes"),
2328 14 : _("%qs bits"),
2329 14 : _("%qs bytes")))
2330 : {
2331 14 : styled_string label = std::move (*p.get ());
2332 28 : w->add_range (m_btm.get_table_x_for_range
2333 28 : (access_range (lower.m_next,
2334 : upper.m_start,
2335 14 : *mgr)),
2336 : std::move (label),
2337 : style::id_plain);
2338 14 : }
2339 125 : }
2340 :
2341 : styled_string
2342 72 : make_warning_string (styled_string &&text)
2343 : {
2344 72 : styled_string result;
2345 72 : if (!m_theme.emojis_p ())
2346 71 : return std::move (text);
2347 :
2348 1 : result.append (styled_string (0x26A0, /* U+26A0 WARNING SIGN. */
2349 1 : true));
2350 : /* U+26A0 WARNING SIGN has East_Asian_Width == Neutral, but in its
2351 : emoji variant is printed (by vte at least) with a 2nd half
2352 : overlapping the next char. Hence we add two spaces here: a space
2353 : to be covered by this overlap, plus another space of padding. */
2354 1 : result.append (styled_string (m_sm, " "));
2355 1 : result.append (std::move (text));
2356 1 : return result;
2357 72 : }
2358 :
2359 : /* Add a ruler child widet showing valid, invalid, and gaps. */
2360 68 : void add_valid_vs_invalid_ruler ()
2361 : {
2362 68 : LOG_SCOPE (m_logger);
2363 :
2364 68 : x_aligned_x_ruler_widget *w
2365 68 : = new x_aligned_x_ruler_widget (*this, m_theme);
2366 :
2367 68 : access_range invalid_before_bits;
2368 68 : if (m_op.maybe_get_invalid_before_bits (&invalid_before_bits))
2369 : {
2370 15 : if (m_logger)
2371 0 : invalid_before_bits.log ("invalid_before_bits", *m_logger);
2372 15 : bit_size_expr num_before_bits
2373 15 : (invalid_before_bits.get_size (m_op.get_manager ()));
2374 15 : std::unique_ptr<styled_string> label;
2375 15 : if (m_op.m_dir == access_direction::read)
2376 8 : label = num_before_bits.maybe_get_formatted_str
2377 16 : (m_sm, m_op.m_model,
2378 8 : _("under-read of %wi bit"),
2379 8 : _("under-read of %wi bits"),
2380 8 : _("under-read of %wi byte"),
2381 8 : _("under-read of %wi bytes"),
2382 8 : _("under-read of %qs bits"),
2383 16 : _("under-read of %qs bytes"));
2384 : else
2385 7 : label = num_before_bits.maybe_get_formatted_str
2386 14 : (m_sm, m_op.m_model,
2387 7 : _("underwrite of %wi bit"),
2388 7 : _("underwrite of %wi bits"),
2389 7 : _("underwrite of %wi byte"),
2390 7 : _("underwrite of %wi bytes"),
2391 7 : _("underwrite of %qs bits"),
2392 14 : _("underwrite of %qs bytes"));
2393 15 : if (label)
2394 15 : w->add_range (m_btm.get_table_x_for_range (invalid_before_bits),
2395 30 : make_warning_string (std::move (*label)),
2396 15 : m_invalid_style_id);
2397 15 : }
2398 : else
2399 : {
2400 53 : if (m_logger)
2401 0 : m_logger->log ("no invalid_before_bits");
2402 : }
2403 :
2404 : /* It would be nice to be able to use std::optional<access_range> here,
2405 : but std::optional is C++17. */
2406 68 : bool got_valid_bits = false;
2407 68 : access_range valid_bits (m_op.get_valid_bits ());
2408 68 : bit_size_expr num_valid_bits (valid_bits.get_size (m_op.get_manager ()));
2409 68 : if (m_logger)
2410 0 : valid_bits.log ("valid_bits", *m_logger);
2411 :
2412 68 : got_valid_bits = true;
2413 68 : maybe_add_gap (w, invalid_before_bits, valid_bits);
2414 :
2415 68 : std::unique_ptr<styled_string> label;
2416 68 : if (m_op.m_dir == access_direction::read)
2417 34 : label = num_valid_bits.maybe_get_formatted_str (m_sm,
2418 17 : m_op.m_model,
2419 17 : _("size: %wi bit"),
2420 17 : _("size: %wi bits"),
2421 17 : _("size: %wi byte"),
2422 17 : _("size: %wi bytes"),
2423 17 : _("size: %qs bits"),
2424 34 : _("size: %qs bytes"));
2425 : else
2426 51 : label
2427 102 : = num_valid_bits.maybe_get_formatted_str (m_sm,
2428 51 : m_op.m_model,
2429 51 : _("capacity: %wi bit"),
2430 51 : _("capacity: %wi bits"),
2431 51 : _("capacity: %wi byte"),
2432 51 : _("capacity: %wi bytes"),
2433 51 : _("capacity: %qs bits"),
2434 102 : _("capacity: %qs bytes"));
2435 68 : if (label)
2436 67 : w->add_range (m_btm.get_table_x_for_range (m_op.get_valid_bits ()),
2437 67 : std::move (*label),
2438 67 : m_valid_style_id);
2439 :
2440 68 : access_range invalid_after_bits;
2441 68 : if (m_op.maybe_get_invalid_after_bits (&invalid_after_bits))
2442 : {
2443 57 : if (got_valid_bits)
2444 57 : maybe_add_gap (w, valid_bits, invalid_after_bits);
2445 :
2446 57 : if (m_logger)
2447 0 : invalid_before_bits.log ("invalid_after_bits", *m_logger);
2448 :
2449 57 : bit_size_expr num_after_bits
2450 57 : (invalid_after_bits.get_size (m_op.get_manager ()));
2451 57 : std::unique_ptr<styled_string> label;
2452 57 : if (m_op.m_dir == access_direction::read)
2453 11 : label = num_after_bits.maybe_get_formatted_str
2454 22 : (m_sm, m_op.m_model,
2455 11 : _("over-read of %wi bit"),
2456 11 : _("over-read of %wi bits"),
2457 11 : _("over-read of %wi byte"),
2458 11 : _("over-read of %wi bytes"),
2459 11 : _("over-read of %qs bits"),
2460 22 : _("over-read of %qs bytes"));
2461 : else
2462 46 : label = num_after_bits.maybe_get_formatted_str
2463 92 : (m_sm, m_op.m_model,
2464 46 : _("overflow of %wi bit"),
2465 46 : _("overflow of %wi bits"),
2466 46 : _("overflow of %wi byte"),
2467 46 : _("overflow of %wi bytes"),
2468 46 : _("overflow of %qs bits"),
2469 92 : _("overflow of %qs bytes"));
2470 57 : if (label)
2471 57 : w->add_range (m_btm.get_table_x_for_range (invalid_after_bits),
2472 114 : make_warning_string (std::move (*label)),
2473 57 : m_invalid_style_id);
2474 57 : }
2475 : else
2476 : {
2477 11 : if (m_logger)
2478 0 : m_logger->log ("no invalid_after_bits");
2479 : }
2480 :
2481 68 : add_child (std::unique_ptr<widget> (w));
2482 68 : }
2483 :
2484 : /* Subroutine of calc_req_size.
2485 : Try to allocate surplus canvas width to table columns to make the
2486 : per table-column canvas widths closer to being to scale.
2487 : See e.g.:
2488 : https://en.wikipedia.org/wiki/Fair_item_allocation
2489 : https://en.wikipedia.org/wiki/Mathematics_of_apportionment
2490 : */
2491 68 : void adjust_to_scale ()
2492 : {
2493 68 : LOG_SCOPE (m_logger);
2494 68 : const unsigned num_columns = m_btm.get_num_columns ();
2495 68 : std::vector<bit_offset_t> bit_sizes (num_columns);
2496 624 : for (unsigned table_x = 0; table_x < num_columns; table_x++)
2497 : {
2498 556 : access_range range_for_column (nullptr, bit_range (0, 0));
2499 556 : if (m_btm.maybe_get_access_range_for_table_x (table_x,
2500 : &range_for_column))
2501 : {
2502 455 : bit_size_t size_in_bits;
2503 455 : if (!range_for_column.get_size_in_bits (&size_in_bits))
2504 60 : size_in_bits = BITS_PER_UNIT; // arbitrary non-zero value
2505 455 : gcc_assert (size_in_bits > 0);
2506 455 : bit_sizes[table_x] = size_in_bits;
2507 : }
2508 : else
2509 101 : bit_sizes[table_x] = 0;
2510 : }
2511 :
2512 725 : while (adjust_to_scale_once (bit_sizes))
2513 : {
2514 : }
2515 68 : }
2516 725 : bool adjust_to_scale_once (const std::vector<bit_offset_t> &bit_sizes)
2517 : {
2518 725 : LOG_SCOPE (m_logger);
2519 :
2520 725 : const unsigned num_columns = m_btm.get_num_columns ();
2521 :
2522 : /* Find the total canvas width currently required.
2523 : Require one extra canvas column for the right-hand border
2524 : of the table. */
2525 725 : int total_width = 1;
2526 4838 : for (unsigned table_x = 0; table_x < num_columns; table_x++)
2527 : {
2528 4113 : int canvas_w = m_col_widths->m_requirements[table_x];
2529 4113 : gcc_assert (canvas_w >= 0);
2530 4113 : total_width += canvas_w + 1;
2531 : }
2532 :
2533 725 : const int max_width = param_analyzer_text_art_ideal_canvas_width;
2534 725 : if (total_width >= max_width)
2535 : {
2536 68 : if (m_logger)
2537 0 : m_logger->log ("bailing out: total_width=%i ,>= max_width (%i)\n",
2538 : total_width, max_width);
2539 68 : return false;
2540 : }
2541 :
2542 657 : const int fixed_point = 1024;
2543 657 : std::vector<bit_offset_t> canvas_w_per_bit (num_columns);
2544 4214 : for (unsigned table_x = 0; table_x < num_columns; table_x++)
2545 : {
2546 3557 : bit_offset_t bit_size = bit_sizes[table_x];
2547 3557 : if (bit_size > 0)
2548 5192 : canvas_w_per_bit[table_x]
2549 2596 : = (m_col_widths->m_requirements[table_x] * fixed_point) / bit_size;
2550 : else
2551 961 : canvas_w_per_bit[table_x] = INT_MAX;
2552 : }
2553 :
2554 : /* Find the min canvas per bit, and give an extra canvas column to
2555 : the table column that has least. */
2556 657 : size_t min_idx = std::distance (canvas_w_per_bit.begin (),
2557 : std::min_element (canvas_w_per_bit.begin (),
2558 657 : canvas_w_per_bit.end ()));
2559 657 : m_col_widths->m_requirements[min_idx] += 1;
2560 657 : if (m_logger)
2561 0 : m_logger->log ("adding 1 canvas_w to column %i\n", (int)min_idx);
2562 :
2563 657 : return true; // keep going
2564 725 : }
2565 :
2566 : const access_operation &m_op;
2567 : diagnostics::paths::event_id_t m_region_creation_event_id;
2568 : style_manager &m_sm;
2569 : const theme &m_theme;
2570 : logger *m_logger;
2571 : /* In lieu of being able to throw exceptions, a flag to mark this object
2572 : as "invalid". */
2573 : bool m_invalid;
2574 :
2575 : style::id_t m_valid_style_id;
2576 : style::id_t m_invalid_style_id;
2577 :
2578 : valid_region_spatial_item m_valid_region_spatial_item;
2579 : accessed_region_spatial_item m_accessed_region_spatial_item;
2580 : std::unique_ptr<spatial_item> m_written_svalue_spatial_item;
2581 :
2582 : std::unique_ptr<boundaries> m_boundaries;
2583 :
2584 : bit_to_table_map m_btm;
2585 :
2586 : bool m_calc_req_size_called;
2587 :
2588 : /* Column widths shared by all x_aligned_table_widget,
2589 : created once we know how many columns we need. */
2590 : std::unique_ptr<table_dimension_sizes> m_col_widths;
2591 :
2592 : /* All of the child x_aligned_table_widget that share
2593 : column widths. */
2594 : std::vector<x_aligned_table_widget *> m_aligned_table_widgets;
2595 :
2596 : /* Mapping from table_x to canvas_x. */
2597 : std::vector<int> m_col_start_x;
2598 : };
2599 :
2600 : x_ruler
2601 136 : x_aligned_x_ruler_widget::make_x_ruler () const
2602 : {
2603 136 : x_ruler r (x_ruler::label_dir::BELOW);
2604 442 : for (auto& iter : m_labels)
2605 : {
2606 306 : canvas::range_t canvas_x_range
2607 306 : = m_dia_impl.get_canvas_x_range (iter.m_table_x_range);
2608 : /* Include the end-point. */
2609 306 : canvas_x_range.next++;
2610 306 : r.add_label (canvas_x_range, iter.m_text.copy (), iter.m_style_id,
2611 : x_ruler::label_kind::TEXT_WITH_BORDER);
2612 : }
2613 136 : return r;
2614 : }
2615 :
2616 : /* class direction_widget : public leaf_widget. */
2617 :
2618 : /* Paint arrows indicating the direction of the access (read vs write),
2619 : but only in the X-extent corresponding to the region that's actually
2620 : accessed. */
2621 :
2622 : void
2623 68 : direction_widget::paint_to_canvas (canvas &canvas)
2624 : {
2625 68 : const access_range accessed_bits (m_dia_impl.get_op ().get_actual_bits ());
2626 :
2627 68 : const access_range valid_bits (m_dia_impl.get_op ().get_valid_bits ());
2628 :
2629 624 : for (unsigned table_x = 0; table_x < m_btm.get_num_columns (); table_x++)
2630 : {
2631 556 : access_range column_access_range;
2632 556 : if (m_btm.maybe_get_access_range_for_table_x (table_x,
2633 : &column_access_range))
2634 : {
2635 : /* Only paint arrows in the accessed region. */
2636 455 : if (!accessed_bits.contains_p (column_access_range))
2637 175 : continue;
2638 :
2639 : /* Are we within the valid region? */
2640 280 : const bool is_valid (valid_bits.contains_p (column_access_range));
2641 280 : const style::id_t style_id
2642 280 : = m_dia_impl.get_style_id_for_validity (is_valid);
2643 280 : const canvas::range_t x_canvas_range
2644 280 : = m_dia_impl.get_canvas_x_range (table::range_t (table_x,
2645 280 : table_x + 1));
2646 280 : const int canvas_x = x_canvas_range.get_midpoint ();
2647 280 : m_dia_impl.get_theme ().paint_y_arrow
2648 280 : (canvas,
2649 : canvas_x,
2650 : canvas::range_t (get_y_range ()),
2651 280 : (m_dia_impl.get_op ().m_dir == access_direction::read
2652 280 : ? theme::y_arrow_dir::UP
2653 : : theme::y_arrow_dir::DOWN),
2654 : style_id);
2655 : }
2656 : }
2657 68 : }
2658 :
2659 : /* class access_diagram : public text_art::wrapper_widget. */
2660 :
2661 : /* To hide the implementation details, this is merely a wrapper around
2662 : an access_diagram_impl. */
2663 :
2664 72 : access_diagram::access_diagram (const access_operation &op,
2665 : diagnostics::paths::event_id_t region_creation_event_id,
2666 : style_manager &sm,
2667 : const theme &theme,
2668 72 : logger *logger)
2669 : : wrapper_widget
2670 72 : (std::make_unique <access_diagram_impl> (op,
2671 : region_creation_event_id,
2672 : sm,
2673 : theme,
2674 72 : logger))
2675 : {
2676 72 : }
2677 :
2678 : #if CHECKING_P
2679 :
2680 : namespace selftest {
2681 :
2682 : /* Implementation detail of ASSERT_EQ_TYPELESS_INTEGER. */
2683 :
2684 : static void
2685 8 : assert_eq_typeless_integer (const location &loc,
2686 : const svalue *sval,
2687 : int expected_int_val)
2688 : {
2689 8 : ASSERT_NE_AT (loc, sval, nullptr);
2690 8 : ASSERT_EQ_AT (loc, sval->get_kind (), SK_CONSTANT);
2691 8 : ASSERT_EQ_AT (loc,
2692 : wi::to_offset (sval->maybe_get_constant ()),
2693 : expected_int_val);
2694 8 : ASSERT_EQ_AT (loc, sval->get_type (), NULL_TREE);
2695 8 : }
2696 :
2697 : /* Assert that SVAL is a constant_svalue equal to EXPECTED_INT_VAL,
2698 : with NULL_TREE as its type. */
2699 :
2700 : #define ASSERT_EQ_TYPELESS_INTEGER(SVAL, EXPECTED_INT_VAL) \
2701 : SELFTEST_BEGIN_STMT \
2702 : assert_eq_typeless_integer ((SELFTEST_LOCATION), \
2703 : (SVAL), \
2704 : (EXPECTED_INT_VAL)); \
2705 : SELFTEST_END_STMT
2706 :
2707 :
2708 : /* Various tests of bit_size_expr::maybe_get_as_bytes. */
2709 :
2710 : static void
2711 4 : test_bit_size_expr_to_bytes ()
2712 : {
2713 4 : region_model_manager mgr;
2714 :
2715 : /* 40 bits: should be 5 bytes. */
2716 4 : {
2717 4 : bit_size_expr num_bits (*mgr.get_or_create_int_cst (NULL_TREE, 40));
2718 4 : const svalue *as_bytes = num_bits.maybe_get_as_bytes (mgr);
2719 4 : ASSERT_EQ_TYPELESS_INTEGER (as_bytes, 5);
2720 : }
2721 :
2722 : /* 41 bits: should not convert to bytes. */
2723 4 : {
2724 4 : bit_size_expr num_bits (*mgr.get_or_create_int_cst (NULL_TREE, 41));
2725 4 : const svalue *as_bytes = num_bits.maybe_get_as_bytes (mgr);
2726 4 : ASSERT_EQ (as_bytes, nullptr);
2727 : }
2728 :
2729 4 : tree n = build_global_decl ("n", size_type_node);
2730 :
2731 4 : const svalue *init_n
2732 4 : = mgr.get_or_create_initial_value (mgr.get_region_for_global (n));
2733 :
2734 4 : const svalue *n_times_8
2735 4 : = mgr.get_or_create_binop (NULL_TREE, MULT_EXPR,
2736 : init_n,
2737 4 : mgr.get_or_create_int_cst (NULL_TREE, 8));
2738 :
2739 : /* (n * 8) bits should be n bytes */
2740 4 : {
2741 4 : bit_size_expr num_bits (*n_times_8);
2742 4 : const svalue *as_bytes = num_bits.maybe_get_as_bytes (mgr);
2743 4 : ASSERT_EQ (as_bytes, mgr.get_or_create_cast (NULL_TREE, init_n));
2744 : }
2745 :
2746 : /* (n * 8) + 16 bits should be n + 2 bytes */
2747 4 : {
2748 4 : bit_size_expr num_bits
2749 4 : (*mgr.get_or_create_binop (NULL_TREE, PLUS_EXPR,
2750 : n_times_8,
2751 4 : mgr.get_or_create_int_cst (NULL_TREE, 16)));
2752 4 : const svalue *as_bytes = num_bits.maybe_get_as_bytes (mgr);
2753 4 : ASSERT_EQ (as_bytes->get_kind (), SK_BINOP);
2754 4 : const binop_svalue *binop = as_bytes->dyn_cast_binop_svalue ();
2755 4 : ASSERT_EQ (binop->get_op (), PLUS_EXPR);
2756 4 : ASSERT_EQ (binop->get_arg0 (), mgr.get_or_create_cast (NULL_TREE, init_n));
2757 4 : ASSERT_EQ_TYPELESS_INTEGER (binop->get_arg1 (), 2);
2758 : }
2759 4 : }
2760 :
2761 : /* Run all of the selftests within this file. */
2762 :
2763 : void
2764 4 : analyzer_access_diagram_cc_tests ()
2765 : {
2766 4 : test_bit_size_expr_to_bytes ();
2767 4 : }
2768 :
2769 : } // namespace selftest
2770 :
2771 : #endif /* CHECKING_P */
2772 :
2773 : } // namespace ana
2774 :
2775 : #endif /* #if ENABLE_ANALYZER */
|