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 559 : access_range::empty_p () const
509 : {
510 559 : bit_range concrete_bits (0, 0);
511 559 : if (!as_concrete_bit_range (&concrete_bits))
512 : return false;
513 465 : 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 1314 : access_operation::get_valid_bits () const
554 : {
555 1314 : const svalue *capacity_in_bytes_sval = m_model.get_capacity (m_base_region);
556 1314 : return access_range
557 1314 : (region_offset::make_concrete (m_base_region, 0),
558 1314 : region_offset::make_byte_offset (m_base_region, capacity_in_bytes_sval),
559 2628 : *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 concrete_binding_map &map = m_compound_sval.get_concrete_bindings ();
1216 11 : auto_vec <const binding_key *> binding_keys;
1217 37 : for (auto iter : map)
1218 : {
1219 26 : const bit_range &key = iter.first;
1220 26 : const svalue *bound_sval = iter.second;
1221 26 : access_range range (nullptr, key);
1222 26 : if (std::unique_ptr<spatial_item> child
1223 : = make_existing_svalue_spatial_item (bound_sval,
1224 : range,
1225 26 : theme))
1226 26 : m_children.push_back (std::move (child));
1227 : }
1228 11 : }
1229 :
1230 11 : void add_boundaries (boundaries &out, logger *logger) const final override
1231 : {
1232 11 : LOG_SCOPE (logger);
1233 22 : for (auto &iter : m_children)
1234 11 : iter->add_boundaries (out, logger);
1235 11 : }
1236 :
1237 11 : table make_table (const bit_to_table_map &btm,
1238 : style_manager &sm) const final override
1239 : {
1240 11 : std::vector<table> child_tables;
1241 11 : int max_rows = 0;
1242 22 : for (auto &iter : m_children)
1243 : {
1244 11 : table child_table (iter->make_table (btm, sm));
1245 11 : max_rows = MAX (max_rows, child_table.get_size ().h);
1246 11 : child_tables.push_back (std::move (child_table));
1247 11 : }
1248 11 : table t (table::size_t (btm.get_num_columns (), max_rows));
1249 22 : for (auto &&child_table : child_tables)
1250 11 : t.add_other_table (std::move (child_table),
1251 11 : table::coord_t (0, 0));
1252 22 : return t;
1253 11 : }
1254 :
1255 : private:
1256 : const compound_svalue &m_compound_sval;
1257 : std::vector<std::unique_ptr<spatial_item>> m_children;
1258 : };
1259 :
1260 : /* Loop through the TABLE_X_RANGE columns of T, adding
1261 : cells containing "..." in any unoccupied ranges of table cell. */
1262 :
1263 : static void
1264 38 : add_ellipsis_to_gaps (table &t,
1265 : style_manager &sm,
1266 : const table::range_t &table_x_range,
1267 : const table::range_t &table_y_range)
1268 : {
1269 38 : int table_x = table_x_range.get_min ();
1270 80 : while (table_x < table_x_range.get_next ())
1271 : {
1272 : /* Find a run of unoccupied table cells. */
1273 : const int start_table_x = table_x;
1274 236 : while (table_x < table_x_range.get_next ()
1275 236 : && !t.get_placement_at (table::coord_t (table_x,
1276 205 : table_y_range.get_min ())))
1277 194 : table_x++;
1278 42 : const table::range_t unoccupied_x_range (start_table_x, table_x);
1279 42 : if (unoccupied_x_range.get_size () > 0)
1280 42 : t.set_cell_span (table::rect_t (unoccupied_x_range, table_y_range),
1281 84 : styled_string (sm, "..."));
1282 : /* Skip occupied table cells. */
1283 53 : while (table_x < table_x_range.get_next ()
1284 53 : && t.get_placement_at (table::coord_t (table_x,
1285 21 : table_y_range.get_min ())))
1286 11 : table_x++;
1287 : }
1288 38 : }
1289 :
1290 : /* Subclass of spatial_item for visualizing the region of memory
1291 : that's valid to access relative to the base region of region accessed in
1292 : the operation. */
1293 :
1294 : class valid_region_spatial_item : public spatial_item
1295 : {
1296 : public:
1297 72 : valid_region_spatial_item (const access_operation &op,
1298 : diagnostics::paths::event_id_t region_creation_event_id,
1299 : const theme &theme)
1300 72 : : m_op (op),
1301 72 : m_region_creation_event_id (region_creation_event_id),
1302 72 : m_boundaries (nullptr),
1303 144 : m_existing_sval (op.m_model.get_store_value (op.m_base_region, nullptr)),
1304 72 : m_existing_sval_spatial_item
1305 : (make_existing_svalue_spatial_item (m_existing_sval,
1306 72 : op.get_valid_bits (),
1307 72 : theme))
1308 : {
1309 72 : }
1310 :
1311 72 : void add_boundaries (boundaries &out, logger *logger) const final override
1312 : {
1313 72 : LOG_SCOPE (logger);
1314 72 : m_boundaries = &out;
1315 72 : access_range valid_bits = m_op.get_valid_bits ();
1316 72 : if (logger)
1317 : {
1318 0 : logger->start_log_line ();
1319 0 : logger->log_partial ("valid bits: ");
1320 0 : valid_bits.dump_to_pp (logger->get_printer (), true);
1321 0 : logger->end_log_line ();
1322 : }
1323 72 : out.add (valid_bits, boundaries::kind::HARD);
1324 :
1325 72 : if (m_existing_sval_spatial_item)
1326 : {
1327 13 : if (logger)
1328 : {
1329 0 : logger->start_log_line ();
1330 0 : logger->log_partial ("existing svalue: ");
1331 0 : m_existing_sval->dump_to_pp (logger->get_printer (), true);
1332 0 : logger->end_log_line ();
1333 : }
1334 13 : m_existing_sval_spatial_item->add_boundaries (out, logger);
1335 : }
1336 :
1337 : /* Support for showing first and final element in array types. */
1338 72 : if (tree base_type = m_op.m_base_region->get_type ())
1339 39 : if (TREE_CODE (base_type) == ARRAY_TYPE)
1340 : {
1341 39 : if (logger)
1342 0 : logger->log ("showing first and final element in array type");
1343 39 : region_model_manager *mgr = m_op.m_model.get_manager ();
1344 39 : tree domain = TYPE_DOMAIN (base_type);
1345 39 : if (domain && TYPE_MIN_VALUE (domain) && TYPE_MAX_VALUE (domain))
1346 : {
1347 38 : const svalue *min_idx_sval
1348 38 : = mgr->get_or_create_constant_svalue (TYPE_MIN_VALUE (domain));
1349 38 : const svalue *max_idx_sval
1350 38 : = mgr->get_or_create_constant_svalue (TYPE_MAX_VALUE (domain));
1351 38 : const region *min_element =
1352 38 : mgr->get_element_region (m_op.m_base_region,
1353 38 : TREE_TYPE (base_type),
1354 : min_idx_sval);
1355 38 : out.add (*min_element, mgr, boundaries::kind::SOFT);
1356 38 : const region *max_element =
1357 38 : mgr->get_element_region (m_op.m_base_region,
1358 38 : TREE_TYPE (base_type),
1359 : max_idx_sval);
1360 38 : out.add (*max_element, mgr, boundaries::kind::SOFT);
1361 : }
1362 : }
1363 72 : }
1364 :
1365 : /* Subroutine of make_table when base region has ARRAY_TYPE. */
1366 39 : void add_array_elements_to_table (table &t,
1367 : const bit_to_table_map &btm,
1368 : style_manager &sm) const
1369 : {
1370 39 : tree base_type = m_op.m_base_region->get_type ();
1371 39 : gcc_assert (TREE_CODE (base_type) == ARRAY_TYPE);
1372 39 : gcc_assert (m_boundaries != nullptr);
1373 :
1374 39 : tree domain = TYPE_DOMAIN (base_type);
1375 39 : if (!(domain && TYPE_MIN_VALUE (domain) && TYPE_MAX_VALUE (domain)))
1376 1 : return;
1377 :
1378 38 : const int table_y = 0;
1379 38 : const int table_h = 1;
1380 38 : const table::range_t table_y_range (table_y, table_y + table_h);
1381 :
1382 38 : t.add_row ();
1383 :
1384 38 : const table::range_t min_x_range
1385 38 : = maybe_add_array_index_to_table (t, btm, sm, table_y_range,
1386 38 : TYPE_MIN_VALUE (domain));
1387 38 : const table::range_t max_x_range
1388 38 : = maybe_add_array_index_to_table (t, btm, sm, table_y_range,
1389 38 : TYPE_MAX_VALUE (domain));
1390 :
1391 38 : if (TREE_TYPE (base_type) == char_type_node)
1392 : {
1393 : /* For a char array,: if there are any hard boundaries in
1394 : m_boundaries that are *within* the valid region,
1395 : then show those index values. */
1396 21 : std::vector<region_offset> hard_boundaries
1397 21 : = m_boundaries->get_hard_boundaries_in_range
1398 21 : (tree_to_shwi (TYPE_MIN_VALUE (domain)),
1399 42 : tree_to_shwi (TYPE_MAX_VALUE (domain)));
1400 53 : for (auto &offset : hard_boundaries)
1401 : {
1402 32 : const int table_x = btm.get_table_x_for_offset (offset);
1403 32 : if (!offset.concrete_p ())
1404 0 : continue;
1405 32 : byte_offset_t byte;
1406 32 : if (!offset.get_concrete_byte_offset (&byte))
1407 0 : continue;
1408 32 : table::range_t table_x_range (table_x, table_x + 1);
1409 64 : t.maybe_set_cell_span (table::rect_t (table_x_range,
1410 32 : table_y_range),
1411 64 : fmt_styled_string (sm, "[%wi]",
1412 : byte.to_shwi ()));
1413 : }
1414 21 : }
1415 :
1416 38 : add_ellipsis_to_gaps (t, sm,
1417 38 : table::range_t (min_x_range.get_next (),
1418 38 : max_x_range.get_min ()),
1419 : table_y_range);
1420 : }
1421 :
1422 : table::range_t
1423 76 : maybe_add_array_index_to_table (table &t,
1424 : const bit_to_table_map &btm,
1425 : style_manager &sm,
1426 : const table::range_t table_y_range,
1427 : tree idx_cst) const
1428 : {
1429 76 : region_model_manager * const mgr = m_op.get_manager ();
1430 76 : tree base_type = m_op.m_base_region->get_type ();
1431 76 : const svalue *idx_sval
1432 76 : = mgr->get_or_create_constant_svalue (idx_cst);
1433 76 : const region *element_reg = mgr->get_element_region (m_op.m_base_region,
1434 76 : TREE_TYPE (base_type),
1435 : idx_sval);
1436 76 : const access_range element_range (*element_reg, mgr);
1437 76 : const table::range_t element_x_range
1438 76 : = btm.get_table_x_for_range (element_range);
1439 :
1440 76 : t.maybe_set_cell_span (table::rect_t (element_x_range,
1441 76 : table_y_range),
1442 152 : fmt_styled_string (sm, "[%E]", idx_cst));
1443 :
1444 76 : return element_x_range;
1445 : }
1446 :
1447 68 : table make_table (const bit_to_table_map &btm,
1448 : style_manager &sm) const final override
1449 : {
1450 68 : table t (table::size_t (btm.get_num_columns (), 0));
1451 :
1452 68 : if (tree base_type = m_op.m_base_region->get_type ())
1453 39 : if (TREE_CODE (base_type) == ARRAY_TYPE)
1454 39 : add_array_elements_to_table (t, btm, sm);
1455 :
1456 : /* Make use of m_existing_sval_spatial_item, if any. */
1457 68 : if (m_existing_sval_spatial_item)
1458 : {
1459 13 : table table_for_existing
1460 13 : = m_existing_sval_spatial_item->make_table (btm, sm);
1461 13 : const int table_y = t.add_rows (table_for_existing.get_size ().h);
1462 13 : t.add_other_table (std::move (table_for_existing),
1463 13 : table::coord_t (0, table_y));
1464 13 : }
1465 :
1466 68 : access_range valid_bits = m_op.get_valid_bits ();
1467 68 : const int table_y = t.add_row ();
1468 68 : const int table_h = 1;
1469 68 : table::rect_t rect = btm.get_table_rect (valid_bits, table_y, table_h);
1470 68 : styled_string s;
1471 68 : switch (m_op.m_base_region->get_kind ())
1472 : {
1473 0 : default:
1474 0 : s = styled_string (sm, _("region"));
1475 0 : break;
1476 37 : case RK_DECL:
1477 37 : {
1478 37 : const decl_region *decl_reg
1479 37 : = as_a <const decl_region *> (m_op.m_base_region);
1480 37 : tree decl = decl_reg->get_decl ();
1481 37 : s = fmt_styled_string (sm, "%qE (type: %qT)",
1482 : decl,
1483 74 : TREE_TYPE (decl));
1484 : }
1485 37 : break;
1486 17 : case RK_HEAP_ALLOCATED:
1487 17 : {
1488 17 : if (m_region_creation_event_id.known_p ())
1489 34 : s = fmt_styled_string (sm, _("buffer allocated on heap at %@"),
1490 17 : &m_region_creation_event_id);
1491 : else
1492 0 : s = styled_string (sm, _("heap-allocated buffer"));
1493 : }
1494 : break;
1495 12 : case RK_ALLOCA:
1496 12 : {
1497 12 : if (m_region_creation_event_id.known_p ())
1498 24 : s = fmt_styled_string (sm, _("buffer allocated on stack at %@"),
1499 12 : &m_region_creation_event_id);
1500 : else
1501 0 : s = styled_string (sm, _("stack-allocated buffer"));
1502 : }
1503 : break;
1504 2 : case RK_STRING:
1505 2 : {
1506 2 : const string_region *string_reg
1507 2 : = as_a <const string_region *> (m_op.m_base_region);
1508 2 : tree string_cst = string_reg->get_string_cst ();
1509 2 : s = fmt_styled_string (sm, _("string literal (type: %qT)"),
1510 4 : TREE_TYPE (string_cst));
1511 : }
1512 2 : break;
1513 : }
1514 68 : t.set_cell_span (rect, std::move (s));
1515 :
1516 68 : return t;
1517 68 : }
1518 :
1519 : private:
1520 : const access_operation &m_op;
1521 : diagnostics::paths::event_id_t m_region_creation_event_id;
1522 : mutable const boundaries *m_boundaries;
1523 : const svalue *m_existing_sval;
1524 : std::unique_ptr<spatial_item> m_existing_sval_spatial_item;
1525 : };
1526 :
1527 : /* Subclass of spatial_item for visualizing the region of memory
1528 : that's actually accessed by the read or write, for reads and
1529 : for write cases where we don't know the svalue written. */
1530 :
1531 : class accessed_region_spatial_item : public spatial_item
1532 : {
1533 : public:
1534 72 : accessed_region_spatial_item (const access_operation &op) : m_op (op) {}
1535 :
1536 72 : void add_boundaries (boundaries &out, logger *logger) const final override
1537 : {
1538 72 : LOG_SCOPE (logger);
1539 72 : access_range actual_bits = m_op.get_actual_bits ();
1540 72 : if (logger)
1541 : {
1542 0 : logger->start_log_line ();
1543 0 : logger->log_partial ("actual bits: ");
1544 0 : actual_bits.dump_to_pp (logger->get_printer (), true);
1545 0 : logger->end_log_line ();
1546 : }
1547 72 : out.add (actual_bits, boundaries::kind::HARD);
1548 72 : }
1549 :
1550 17 : table make_table (const bit_to_table_map &btm,
1551 : style_manager &sm) const final override
1552 : {
1553 17 : table t (table::size_t (btm.get_num_columns (), 1));
1554 :
1555 17 : access_range actual_bits = m_op.get_actual_bits ();
1556 17 : const int table_y = 0;
1557 17 : const int table_h = 1;
1558 17 : table::rect_t rect = btm.get_table_rect (actual_bits, table_y, table_h);
1559 17 : t.set_cell_span (rect, styled_string (get_label_string (sm)));
1560 :
1561 17 : return t;
1562 : }
1563 :
1564 : private:
1565 17 : styled_string get_label_string (style_manager &sm) const
1566 : {
1567 17 : const access_range accessed_bits (m_op.get_actual_bits ());
1568 17 : return get_access_size_str (sm,
1569 : m_op,
1570 : accessed_bits,
1571 17 : m_op.m_reg.get_type ());
1572 : }
1573 :
1574 : const access_operation &m_op;
1575 : };
1576 :
1577 : /* Subclass of spatial_item for when we know the svalue being written
1578 : to the accessed region.
1579 : Can be subclassed to give visualizations of specific kinds of svalue. */
1580 :
1581 : class written_svalue_spatial_item : public spatial_item
1582 : {
1583 : public:
1584 38 : written_svalue_spatial_item (const access_operation &op,
1585 : const svalue &sval,
1586 : access_range actual_bits)
1587 38 : : m_op (op), m_sval (sval), m_actual_bits (actual_bits)
1588 : {}
1589 :
1590 38 : void add_boundaries (boundaries &out, logger *logger) const override
1591 : {
1592 38 : LOG_SCOPE (logger);
1593 38 : out.add (m_actual_bits, boundaries::kind::HARD);
1594 38 : }
1595 :
1596 34 : table make_table (const bit_to_table_map &btm,
1597 : style_manager &sm) const override
1598 : {
1599 34 : table t (table::size_t (btm.get_num_columns (), 0));
1600 :
1601 34 : const int table_y = t.add_row ();
1602 34 : const int table_h = 1;
1603 34 : table::rect_t rect = btm.get_table_rect (m_actual_bits, table_y, table_h);
1604 34 : t.set_cell_span (rect, styled_string (get_label_string (sm)));
1605 34 : return t;
1606 : }
1607 :
1608 : protected:
1609 34 : styled_string get_label_string (style_manager &sm) const
1610 : {
1611 34 : tree rep_tree = m_op.m_model.get_representative_tree (&m_sval);
1612 34 : if (rep_tree)
1613 : {
1614 30 : if (TREE_CODE (rep_tree) == SSA_NAME)
1615 17 : if (tree var = SSA_NAME_VAR (rep_tree))
1616 30 : rep_tree = var;
1617 30 : switch (TREE_CODE (rep_tree))
1618 : {
1619 : default:
1620 : break;
1621 13 : case INTEGER_CST:
1622 26 : return fmt_styled_string (sm, _("write of %<(%T) %E%>"),
1623 13 : TREE_TYPE (rep_tree),
1624 13 : rep_tree);
1625 :
1626 13 : case PARM_DECL:
1627 13 : case VAR_DECL:
1628 26 : return fmt_styled_string (sm, _("write from %qE (type: %qT)"),
1629 : rep_tree,
1630 13 : TREE_TYPE (rep_tree));
1631 : break;
1632 : }
1633 : }
1634 :
1635 8 : const access_range accessed_bits (m_op.get_actual_bits ());
1636 8 : return get_access_size_str (sm,
1637 : m_op,
1638 : accessed_bits,
1639 8 : m_sval.get_type ());
1640 : }
1641 :
1642 : const access_operation &m_op;
1643 : const svalue &m_sval;
1644 : access_range m_actual_bits;
1645 : };
1646 :
1647 : /* Subclass of svalue_spatial_item for initial_svalue of a string_region
1648 : i.e. for string literals.
1649 :
1650 : There are three cases:
1651 : (a) for long strings, show just the head and tail of the string,
1652 : with an ellipsis:
1653 : +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
1654 : |[0]|[1]|[2]|[3]|[4]|[5]| |[440]|[441]|[442]|[443]|[444]|[445]|
1655 : +---+---+---+---+---+---+ ... +-----+-----+-----+-----+-----+-----+
1656 : |‘L’|‘o’|‘r’|‘e’|‘m’|‘ ’| | ‘o’ | ‘r’ | ‘u’ | ‘m’ | ‘.’ | NUL |
1657 : +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
1658 : | string literal (type: ‘char[446]’) |
1659 : +----------------------------------------------------------------------+
1660 : (b) For sufficiently short strings, show the full string:
1661 : +----------+---------+---------+---------+---------+ +-----------------+
1662 : | [0] | [1] | [2] | [3] | [4] | | [5] |
1663 : +----------+---------+---------+---------+---------+ +-----------------+
1664 : | ‘h’ | ‘e’ | ‘l’ | ‘l’ | ‘o’ | | NUL |
1665 : +----------+---------+---------+---------+---------+-+-----------------+
1666 : | string literal (type: ‘char[6]’) |
1667 : +----------------------------------------------------------------------+
1668 : (c) for non-ASCII strings that are short enough to show the full string,
1669 : show how unicode code points of the bytes decoded as UTF-8:
1670 : +-----+-----+-----+----+----++----+----+----+----+----+----+----+------+
1671 : | [0] | [1] | [2] |[3] |[4] ||[5] |[6] |[7] |[8] |[9] |[10]|[11]| [12] |
1672 : +-----+-----+-----+----+----++----+----+----+----+----+----+----+------+
1673 : |0xe6 |0x96 |0x87 |0xe5|0xad||0x97|0xe5|0x8c|0x96|0xe3|0x81|0x91| 0x00 |
1674 : +-----+-----+-----+----+----++----+----+----+----+----+----+----+------+
1675 : | U+6587 | U+5b57 | U+5316 | U+3051 |U+0000|
1676 : +-----------------+---------------+--------------+--------------+------+
1677 : | string literal (type: ‘char[13]’) |
1678 : +----------------------------------------------------------------------+
1679 : and show the characters themselves if unicode is supported and they are not
1680 : control characters:
1681 : ┌─────┬─────┬─────┬────┬────┐┌────┬────┬────┬────┬────┬────┬────┬──────┐
1682 : │ [0] │ [1] │ [2] │[3] │[4] ││[5] │[6] │[7] │[8] │[9] │[10]│[11]│ [12] │
1683 : ├─────┼─────┼─────┼────┼────┤├────┼────┼────┼────┼────┼────┼────┼──────┤
1684 : │0xe6 │0x96 │0x87 │0xe5│0xad││0x97│0xe5│0x8c│0x96│0xe3│0x81│0x91│ 0x00 │
1685 : ├─────┴─────┴─────┼────┴────┴┴────┼────┴────┴────┼────┴────┴────┼──────┤
1686 : │ U+6587 │ U+5b57 │ U+5316 │ U+3051 │U+0000│
1687 : ├─────────────────┼───────────────┼──────────────┼──────────────┼──────┤
1688 : │ 文 │ 字 │ 化 │ け │ NUL │
1689 : ├─────────────────┴───────────────┴──────────────┴──────────────┴──────┤
1690 : │ string literal (type: ‘char[13]’) │
1691 : └──────────────────────────────────────────────────────────────────────┘
1692 : */
1693 :
1694 : class string_literal_spatial_item : public svalue_spatial_item
1695 : {
1696 : public:
1697 30 : string_literal_spatial_item (const svalue &sval,
1698 : access_range actual_bits,
1699 : const string_region &string_reg,
1700 : const theme &theme,
1701 : enum kind kind)
1702 30 : : svalue_spatial_item (sval, actual_bits, kind),
1703 30 : m_string_reg (string_reg),
1704 30 : m_theme (theme),
1705 30 : m_ellipsis_threshold (param_analyzer_text_art_string_ellipsis_threshold),
1706 30 : m_ellipsis_head_len (param_analyzer_text_art_string_ellipsis_head_len),
1707 30 : m_ellipsis_tail_len (param_analyzer_text_art_string_ellipsis_tail_len),
1708 60 : m_show_full_string (calc_show_full_string ()),
1709 30 : m_show_utf8 (m_show_full_string && !pure_ascii_p ())
1710 : {
1711 30 : }
1712 :
1713 30 : void add_boundaries (boundaries &out, logger *logger) const override
1714 : {
1715 30 : LOG_SCOPE (logger);
1716 30 : out.add (m_bits, m_kind == svalue_spatial_item::kind::WRITTEN
1717 30 : ? boundaries::kind::HARD
1718 : : boundaries::kind::SOFT);
1719 :
1720 30 : tree string_cst = get_string_cst ();
1721 : /* TREE_STRING_LENGTH is sizeof, not strlen. */
1722 30 : if (m_show_full_string)
1723 18 : out.add_all_bytes_in_range (m_bits);
1724 : else
1725 : {
1726 12 : byte_range bytes (0, 0);
1727 12 : bool valid = m_bits.as_concrete_byte_range (&bytes);
1728 12 : gcc_assert (valid);
1729 12 : byte_range head_of_string (bytes.get_start_byte_offset (),
1730 12 : m_ellipsis_head_len);
1731 12 : out.add_all_bytes_in_range (head_of_string);
1732 12 : byte_range tail_of_string
1733 12 : ((bytes.get_start_byte_offset ()
1734 12 : + TREE_STRING_LENGTH (string_cst)
1735 12 : - m_ellipsis_tail_len),
1736 24 : m_ellipsis_tail_len);
1737 12 : out.add_all_bytes_in_range (tail_of_string);
1738 : /* Adding the above pair of ranges will also effectively add
1739 : the boundaries of the range of ellipsized chars, as they're
1740 : exactly in between head_of_string and tail_of_string. */
1741 : }
1742 30 : }
1743 :
1744 30 : table make_table (const bit_to_table_map &btm,
1745 : style_manager &sm) const override
1746 : {
1747 30 : table t (table::size_t (btm.get_num_columns (), 0));
1748 :
1749 30 : const int byte_idx_table_y = (m_kind == svalue_spatial_item::kind::WRITTEN
1750 30 : ? t.add_row ()
1751 : : -1);
1752 30 : const int byte_val_table_y = t.add_row ();
1753 :
1754 30 : byte_range bytes (0, 0);
1755 30 : bool valid = m_bits.as_concrete_byte_range (&bytes);
1756 30 : gcc_assert (valid);
1757 30 : tree string_cst = get_string_cst ();
1758 30 : if (m_show_full_string)
1759 : {
1760 18 : for (byte_offset_t byte_idx_within_cluster
1761 18 : = bytes.get_start_byte_offset ();
1762 155 : byte_idx_within_cluster < bytes.get_next_byte_offset ();
1763 137 : byte_idx_within_cluster = byte_idx_within_cluster + 1)
1764 137 : add_column_for_byte
1765 137 : (t, btm, sm, byte_idx_within_cluster,
1766 274 : byte_idx_within_cluster - bytes.get_start_byte_offset (),
1767 : byte_idx_table_y, byte_val_table_y);
1768 :
1769 18 : if (m_show_utf8)
1770 : {
1771 6 : const bool show_unichars = m_theme.unicode_p ();
1772 6 : const int utf8_code_point_table_y = t.add_row ();
1773 6 : int utf8_character_table_y;
1774 6 : if (show_unichars)
1775 5 : utf8_character_table_y = t.add_row ();
1776 :
1777 : /* We don't actually want the display widths here, but
1778 : it's an easy way to decode UTF-8. */
1779 6 : cpp_char_column_policy policy (8, cpp_wcwidth);
1780 6 : cpp_display_width_computation dw (TREE_STRING_POINTER (string_cst),
1781 6 : TREE_STRING_LENGTH (string_cst),
1782 6 : policy);
1783 47 : while (!dw.done ())
1784 : {
1785 35 : cpp_decoded_char decoded_char;
1786 35 : dw.process_next_codepoint (&decoded_char);
1787 :
1788 35 : if (!decoded_char.m_valid_ch)
1789 0 : continue;
1790 35 : size_t start_byte_idx
1791 35 : = decoded_char.m_start_byte - TREE_STRING_POINTER (string_cst);
1792 35 : byte_size_t size_in_bytes
1793 35 : = decoded_char.m_next_byte - decoded_char.m_start_byte;
1794 35 : byte_range cluster_bytes_for_codepoint
1795 35 : (start_byte_idx + bytes.get_start_byte_offset (),
1796 35 : size_in_bytes);
1797 :
1798 35 : const table::rect_t code_point_table_rect
1799 35 : = btm.get_table_rect (&m_string_reg,
1800 : cluster_bytes_for_codepoint,
1801 : utf8_code_point_table_y, 1);
1802 35 : char buf[100];
1803 35 : sprintf (buf, "U+%04x", decoded_char.m_ch);
1804 35 : t.set_cell_span (code_point_table_rect,
1805 70 : styled_string (sm, buf));
1806 :
1807 35 : if (show_unichars)
1808 : {
1809 30 : const table::rect_t character_table_rect
1810 30 : = btm.get_table_rect (&m_string_reg,
1811 : cluster_bytes_for_codepoint,
1812 : utf8_character_table_y, 1);
1813 30 : if (cpp_is_printable_char (decoded_char.m_ch))
1814 23 : t.set_cell_span (character_table_rect,
1815 46 : styled_string (decoded_char.m_ch));
1816 7 : else if (decoded_char.m_ch == 0)
1817 5 : t.set_cell_span (character_table_rect,
1818 10 : styled_string (sm, "NUL"));
1819 : else
1820 2 : t.set_cell_span (character_table_rect,
1821 4 : styled_string (sm, ""));
1822 : }
1823 : }
1824 : }
1825 : }
1826 : else
1827 : {
1828 : /* Head of string. */
1829 84 : for (int byte_idx = 0; byte_idx < m_ellipsis_head_len; byte_idx++)
1830 144 : add_column_for_byte (t, btm, sm,
1831 144 : byte_idx + bytes.get_start_byte_offset (),
1832 72 : byte_idx,
1833 : byte_idx_table_y, byte_val_table_y);
1834 :
1835 : /* Ellipsis. */
1836 12 : const byte_range ellipsis_bytes
1837 12 : (m_ellipsis_head_len + bytes.get_start_byte_offset (),
1838 12 : TREE_STRING_LENGTH (string_cst)
1839 12 : - (m_ellipsis_head_len + m_ellipsis_tail_len));
1840 12 : const table::rect_t table_rect
1841 : = ((byte_idx_table_y != -1)
1842 12 : ? btm.get_table_rect (&m_string_reg, ellipsis_bytes,
1843 : byte_idx_table_y, 2)
1844 6 : : btm.get_table_rect (&m_string_reg, ellipsis_bytes,
1845 : byte_val_table_y, 1));
1846 12 : t.set_cell_span(table_rect, styled_string (sm, "..."));
1847 :
1848 : /* Tail of string. */
1849 12 : for (int byte_idx
1850 12 : = (TREE_STRING_LENGTH (string_cst) - m_ellipsis_tail_len);
1851 84 : byte_idx < TREE_STRING_LENGTH (string_cst);
1852 : byte_idx++)
1853 144 : add_column_for_byte (t, btm, sm,
1854 144 : byte_idx + bytes.get_start_byte_offset (),
1855 72 : byte_idx,
1856 : byte_idx_table_y, byte_val_table_y);
1857 : }
1858 :
1859 30 : if (m_kind == svalue_spatial_item::kind::WRITTEN)
1860 : {
1861 17 : const int summary_table_y = t.add_row ();
1862 17 : t.set_cell_span (btm.get_table_rect (&m_string_reg, bytes,
1863 : summary_table_y, 1),
1864 34 : fmt_styled_string (sm,
1865 17 : _("string literal (type: %qT)"),
1866 17 : TREE_TYPE (string_cst)));
1867 : }
1868 :
1869 30 : return t;
1870 : }
1871 :
1872 60 : tree get_string_cst () const { return m_string_reg.get_string_cst (); }
1873 :
1874 : private:
1875 30 : bool calc_show_full_string () const
1876 : {
1877 30 : tree string_cst = get_string_cst ();
1878 30 : if (TREE_STRING_LENGTH (string_cst) < m_ellipsis_threshold)
1879 : return true;
1880 13 : if (TREE_STRING_LENGTH (string_cst) <
1881 13 : (m_ellipsis_head_len + m_ellipsis_tail_len))
1882 1 : return true;
1883 : return false;
1884 : }
1885 :
1886 18 : bool pure_ascii_p () const
1887 : {
1888 18 : tree string_cst = get_string_cst ();
1889 104 : for (unsigned byte_idx = 0;
1890 104 : byte_idx < (unsigned) TREE_STRING_LENGTH (string_cst);
1891 : byte_idx++)
1892 : {
1893 92 : unsigned char ch = TREE_STRING_POINTER (string_cst)[byte_idx];
1894 92 : if (ch >= 0x80)
1895 : return false;
1896 : }
1897 : return true;
1898 : }
1899 :
1900 281 : void add_column_for_byte (table &t, const bit_to_table_map &btm,
1901 : style_manager &sm,
1902 : const byte_offset_t byte_idx_within_cluster,
1903 : const byte_offset_t byte_idx_within_string,
1904 : const int byte_idx_table_y,
1905 : const int byte_val_table_y) const
1906 : {
1907 281 : tree string_cst = get_string_cst ();
1908 281 : gcc_assert (byte_idx_within_string >= 0);
1909 281 : gcc_assert (byte_idx_within_string < TREE_STRING_LENGTH (string_cst));
1910 :
1911 281 : const byte_range bytes (byte_idx_within_cluster, 1);
1912 281 : if (byte_idx_table_y != -1)
1913 : {
1914 168 : const table::rect_t idx_table_rect
1915 168 : = btm.get_table_rect (&m_string_reg, bytes, byte_idx_table_y, 1);
1916 336 : t.set_cell_span (idx_table_rect,
1917 336 : fmt_styled_string (sm, "[%wu]",
1918 : byte_idx_within_string.ulow ()));
1919 : }
1920 :
1921 281 : char byte_val
1922 281 : = TREE_STRING_POINTER (string_cst)[byte_idx_within_string.ulow ()];
1923 281 : const table::rect_t val_table_rect
1924 281 : = btm.get_table_rect (&m_string_reg, bytes, byte_val_table_y, 1);
1925 281 : table_cell_content content (make_cell_content_for_byte (sm, byte_val));
1926 281 : t.set_cell_span (val_table_rect, std::move (content));
1927 281 : }
1928 :
1929 281 : table_cell_content make_cell_content_for_byte (style_manager &sm,
1930 : unsigned char byte_val) const
1931 : {
1932 281 : if (!m_show_utf8)
1933 : {
1934 218 : if (byte_val == '\0')
1935 24 : return styled_string (sm, "NUL");
1936 194 : else if (byte_val < 0x80)
1937 194 : if (ISPRINT (byte_val))
1938 190 : return fmt_styled_string (sm, "%qc", byte_val);
1939 : }
1940 67 : char buf[100];
1941 67 : sprintf (buf, "0x%02x", byte_val);
1942 67 : return styled_string (sm, buf);
1943 : }
1944 :
1945 : const string_region &m_string_reg;
1946 : const theme &m_theme;
1947 : const int m_ellipsis_threshold;
1948 : const int m_ellipsis_head_len;
1949 : const int m_ellipsis_tail_len;
1950 : const bool m_show_full_string;
1951 : const bool m_show_utf8;
1952 : };
1953 :
1954 : static std::unique_ptr<spatial_item>
1955 55 : make_written_svalue_spatial_item (const access_operation &op,
1956 : const svalue &sval,
1957 : access_range actual_bits,
1958 : const theme &theme)
1959 : {
1960 55 : if (const initial_svalue *initial_sval = sval.dyn_cast_initial_svalue ())
1961 33 : if (const string_region *string_reg
1962 33 : = initial_sval->get_region ()->dyn_cast_string_region ())
1963 17 : return std::make_unique <string_literal_spatial_item>
1964 17 : (sval, actual_bits,
1965 : *string_reg, theme,
1966 17 : svalue_spatial_item::kind::WRITTEN);
1967 38 : return std::make_unique <written_svalue_spatial_item> (op, sval, actual_bits);
1968 : }
1969 :
1970 : static std::unique_ptr<spatial_item>
1971 98 : make_existing_svalue_spatial_item (const svalue *sval,
1972 : const access_range &bits,
1973 : const theme &theme)
1974 : {
1975 98 : if (!sval)
1976 0 : return nullptr;
1977 :
1978 98 : switch (sval->get_kind ())
1979 : {
1980 60 : default:
1981 60 : return nullptr;
1982 :
1983 27 : case SK_INITIAL:
1984 27 : {
1985 27 : const initial_svalue *initial_sval = (const initial_svalue *)sval;
1986 27 : if (const string_region *string_reg
1987 27 : = initial_sval->get_region ()->dyn_cast_string_region ())
1988 13 : return std::make_unique <string_literal_spatial_item>
1989 13 : (*sval, bits,
1990 : *string_reg, theme,
1991 13 : svalue_spatial_item::kind::EXISTING);
1992 14 : return nullptr;
1993 : }
1994 :
1995 11 : case SK_COMPOUND:
1996 11 : return std::make_unique<compound_svalue_spatial_item>
1997 11 : (*((const compound_svalue *)sval),
1998 : bits,
1999 22 : svalue_spatial_item::kind::EXISTING,
2000 11 : theme);
2001 : }
2002 : }
2003 :
2004 : /* Widget subclass implementing access diagrams. */
2005 :
2006 : class access_diagram_impl : public vbox_widget
2007 : {
2008 : public:
2009 72 : access_diagram_impl (const access_operation &op,
2010 : diagnostics::paths::event_id_t region_creation_event_id,
2011 : style_manager &sm,
2012 : const theme &theme,
2013 : logger *logger)
2014 72 : : m_op (op),
2015 72 : m_region_creation_event_id (region_creation_event_id),
2016 72 : m_sm (sm),
2017 72 : m_theme (theme),
2018 72 : m_logger (logger),
2019 72 : m_invalid (false),
2020 72 : m_valid_region_spatial_item (op, region_creation_event_id, theme),
2021 72 : m_accessed_region_spatial_item (op),
2022 72 : m_btm (),
2023 72 : m_calc_req_size_called (false)
2024 : {
2025 72 : LOG_SCOPE (logger);
2026 :
2027 72 : if (logger)
2028 : {
2029 0 : access_range invalid_before_bits;
2030 0 : if (op.maybe_get_invalid_before_bits (&invalid_before_bits))
2031 0 : invalid_before_bits.log ("invalid before range", *logger);
2032 0 : access_range invalid_after_bits;
2033 0 : if (op.maybe_get_invalid_after_bits (&invalid_after_bits))
2034 0 : invalid_after_bits.log ("invalid after range", *logger);
2035 :
2036 0 : if (op.m_sval_hint)
2037 : {
2038 0 : logger->start_log_line ();
2039 0 : logger->log_partial ("sval_hint: ");
2040 0 : op.m_sval_hint->dump_to_pp (logger->get_printer (), true);
2041 0 : logger->end_log_line ();
2042 : }
2043 : }
2044 :
2045 : /* Register painting styles. */
2046 72 : {
2047 72 : style valid_style (get_style_from_color_cap_name ("valid"));
2048 72 : m_valid_style_id = m_sm.get_or_create_id (valid_style);
2049 :
2050 72 : style invalid_style (get_style_from_color_cap_name ("invalid"));
2051 72 : m_invalid_style_id = m_sm.get_or_create_id (invalid_style);
2052 72 : }
2053 :
2054 72 : if (op.m_sval_hint)
2055 : {
2056 55 : access_range actual_bits = m_op.get_actual_bits ();
2057 55 : m_written_svalue_spatial_item
2058 55 : = make_written_svalue_spatial_item (m_op,
2059 55 : *op.m_sval_hint,
2060 : actual_bits,
2061 55 : m_theme);
2062 : }
2063 :
2064 : /* Two passes:
2065 : First, figure out all of the boundaries of interest.
2066 : Then use that to build child widgets showing the regions of interest,
2067 : with a common tabular layout. */
2068 :
2069 72 : m_boundaries = find_boundaries ();
2070 72 : if (logger)
2071 0 : m_boundaries->log (*logger);
2072 :
2073 : /* Populate m_table_x_for_bit and m_bit_for_table_x.
2074 : Each table column represents the range [offset, next_offset).
2075 : We don't create a column in the table for the final offset, but we
2076 : do populate it, so that looking at the table_x of one beyond the
2077 : final table column gives us the upper bound offset. */
2078 72 : m_btm.populate (*m_boundaries, *m_op.get_manager (), logger);
2079 :
2080 : /* Gracefully reject cases where the boundary sorting has gone wrong
2081 : (due to awkward combinations of symbolic values). */
2082 72 : {
2083 72 : table::range_t actual_bits_x_range
2084 72 : = m_btm.get_table_x_for_range (m_op.get_actual_bits ());
2085 72 : if (actual_bits_x_range.get_size () <= 0)
2086 : {
2087 4 : if (logger)
2088 0 : logger->log ("giving up: bad table columns for actual_bits");
2089 4 : m_invalid = true;
2090 4 : return;
2091 : }
2092 68 : table::range_t valid_bits_x_range
2093 68 : = m_btm.get_table_x_for_range (m_op.get_valid_bits ());
2094 68 : if (valid_bits_x_range.get_size () <= 0)
2095 : {
2096 0 : if (logger)
2097 0 : logger->log ("giving up: bad table columns for valid_bits");
2098 0 : m_invalid = true;
2099 0 : return;
2100 : }
2101 : }
2102 :
2103 68 : m_col_widths
2104 68 : = std::make_unique <table_dimension_sizes> (m_btm.get_num_columns ());
2105 :
2106 : /* Now create child widgets. */
2107 :
2108 68 : if (flag_analyzer_debug_text_art)
2109 : {
2110 1 : table t_headings (make_headings_table ());
2111 2 : add_aligned_child_table (std::move (t_headings));
2112 1 : }
2113 :
2114 68 : if (m_written_svalue_spatial_item)
2115 : {
2116 51 : table t_sval (m_written_svalue_spatial_item->make_table (m_btm, m_sm));
2117 102 : add_aligned_child_table (std::move (t_sval));
2118 51 : }
2119 : else
2120 : {
2121 17 : table t_accessed
2122 17 : (m_accessed_region_spatial_item.make_table (m_btm, m_sm));
2123 34 : add_aligned_child_table (std::move (t_accessed));
2124 17 : }
2125 :
2126 68 : add_direction_widget ();
2127 :
2128 68 : table t_valid (m_valid_region_spatial_item.make_table (m_btm, m_sm));
2129 68 : add_invalid_accesses_to_region_table (t_valid);
2130 68 : add_aligned_child_table (std::move (t_valid));
2131 :
2132 68 : add_valid_vs_invalid_ruler ();
2133 72 : }
2134 :
2135 0 : const char *get_desc () const override
2136 : {
2137 0 : return "access_diagram_impl";
2138 : }
2139 :
2140 72 : canvas::size_t calc_req_size () final override
2141 : {
2142 72 : if (m_invalid)
2143 4 : return canvas::size_t (0, 0);
2144 :
2145 : /* Now compute the size requirements for the tables. */
2146 205 : for (auto iter : m_aligned_table_widgets)
2147 137 : iter->get_cell_sizes ().pass_1 (iter->get_table ());
2148 205 : for (auto iter : m_aligned_table_widgets)
2149 137 : iter->get_cell_sizes ().pass_2 (iter->get_table ());
2150 :
2151 68 : adjust_to_scale();
2152 :
2153 : /* ...and relayout the tables. */
2154 205 : for (auto iter : m_aligned_table_widgets)
2155 137 : iter->recalc_coords ();
2156 :
2157 : /* Populate the canvas_x per table_x. */
2158 68 : m_col_start_x.clear ();
2159 68 : int iter_canvas_x = 0;
2160 624 : for (auto w : m_col_widths->m_requirements)
2161 : {
2162 556 : m_col_start_x.push_back (iter_canvas_x);
2163 556 : iter_canvas_x += w + 1;
2164 : }
2165 68 : m_col_start_x.push_back (iter_canvas_x);
2166 :
2167 68 : m_calc_req_size_called = true;
2168 :
2169 68 : return vbox_widget::calc_req_size ();
2170 : }
2171 :
2172 1172 : int get_canvas_x_for_table_x (int table_x) const
2173 : {
2174 1172 : gcc_assert (m_calc_req_size_called);
2175 1172 : return m_col_start_x[table_x];
2176 : }
2177 :
2178 586 : canvas::range_t get_canvas_x_range (const table::range_t &table_x_range) const
2179 : {
2180 586 : gcc_assert (m_calc_req_size_called);
2181 1172 : return canvas::range_t (get_canvas_x_for_table_x (table_x_range.start),
2182 586 : get_canvas_x_for_table_x (table_x_range.next));
2183 : }
2184 :
2185 416 : const access_operation &get_op () const { return m_op; }
2186 :
2187 280 : style::id_t get_style_id_for_validity (bool is_valid) const
2188 : {
2189 280 : return is_valid ? m_valid_style_id : m_invalid_style_id;
2190 : }
2191 :
2192 280 : const theme &get_theme () const { return m_theme; }
2193 :
2194 : private:
2195 : /* Figure out all of the boundaries of interest when visualizing ths op. */
2196 : std::unique_ptr<boundaries>
2197 72 : find_boundaries () const
2198 : {
2199 72 : auto result
2200 72 : = std::make_unique<boundaries> (*m_op.m_base_region, m_logger);
2201 :
2202 72 : m_valid_region_spatial_item.add_boundaries (*result, m_logger);
2203 72 : m_accessed_region_spatial_item.add_boundaries (*result, m_logger);
2204 72 : if (m_written_svalue_spatial_item)
2205 55 : m_written_svalue_spatial_item->add_boundaries (*result, m_logger);
2206 :
2207 72 : return result;
2208 : }
2209 :
2210 137 : void add_aligned_child_table (table t)
2211 : {
2212 137 : auto w = std::make_unique<x_aligned_table_widget> (std::move (t),
2213 137 : m_theme, *m_col_widths);
2214 137 : m_aligned_table_widgets.push_back (w.get ());
2215 137 : add_child (std::move (w));
2216 137 : }
2217 :
2218 : /* Create a table showing headings for use by -fanalyzer-debug-text-art, for
2219 : example:
2220 : +---------+-----------+-----------+---+--------------------------------+
2221 : | tc0 | tc1 | tc2 |tc3| tc4 |
2222 : +---------+-----------+-----------+---+--------------------------------+
2223 : |bytes 0-3|bytes 4-35 |bytes 36-39| | bytes 40-43 |
2224 : +---------+-----------+-----------+ +--------------------------------+
2225 : which has:
2226 : - a row showing the table column numbers, labelled "tc0", "tc1", etc
2227 : - a row showing the memory range of each table column that has one. */
2228 :
2229 1 : table make_headings_table () const
2230 : {
2231 1 : table t (table::size_t (m_btm.get_num_columns (), 2));
2232 :
2233 6 : for (int table_x = 0; table_x < t.get_size ().w; table_x++)
2234 : {
2235 5 : const int table_y = 0;
2236 5 : t.set_cell (table::coord_t (table_x, table_y),
2237 10 : fmt_styled_string (m_sm, "tc%i", table_x));
2238 : }
2239 6 : for (int table_x = 0; table_x < t.get_size ().w; table_x++)
2240 : {
2241 5 : const int table_y = 1;
2242 5 : access_range range_for_column (nullptr, bit_range (0, 0));
2243 5 : if (m_btm.maybe_get_access_range_for_table_x (table_x,
2244 : &range_for_column))
2245 : {
2246 4 : pretty_printer pp;
2247 4 : pp_format_decoder (&pp) = default_tree_printer;
2248 4 : range_for_column.dump_to_pp (&pp, true);
2249 4 : t.set_cell (table::coord_t (table_x, table_y),
2250 8 : styled_string (m_sm, pp_formatted_text (&pp)));
2251 4 : }
2252 : }
2253 :
2254 1 : return t;
2255 : }
2256 :
2257 68 : void add_direction_widget ()
2258 : {
2259 68 : add_child (std::make_unique<direction_widget> (*this, m_btm));
2260 68 : }
2261 :
2262 68 : void add_invalid_accesses_to_region_table (table &t_region)
2263 : {
2264 68 : gcc_assert (t_region.get_size ().w == (int)m_btm.get_num_columns ());
2265 :
2266 68 : const int table_y = 0;
2267 68 : const int table_h = t_region.get_size ().h;
2268 :
2269 68 : access_range invalid_before_bits;
2270 68 : if (m_op.maybe_get_invalid_before_bits (&invalid_before_bits))
2271 : {
2272 15 : t_region.set_cell_span (m_btm.get_table_rect (invalid_before_bits,
2273 : table_y, table_h),
2274 30 : styled_string (m_sm,
2275 15 : _("before valid range")));
2276 : }
2277 68 : access_range invalid_after_bits;
2278 68 : if (m_op.maybe_get_invalid_after_bits (&invalid_after_bits))
2279 : {
2280 57 : t_region.set_cell_span (m_btm.get_table_rect (invalid_after_bits,
2281 : table_y, table_h),
2282 114 : styled_string (m_sm,
2283 57 : _("after valid range")));
2284 : }
2285 68 : }
2286 :
2287 125 : void maybe_add_gap (x_aligned_x_ruler_widget *w,
2288 : const access_range &lower,
2289 : const access_range &upper) const
2290 : {
2291 125 : LOG_SCOPE (m_logger);
2292 125 : if (m_logger)
2293 : {
2294 0 : lower.log ("lower", *m_logger);
2295 0 : upper.log ("upper", *m_logger);
2296 : }
2297 125 : region_model_manager *mgr = m_op.get_manager ();
2298 125 : const svalue &lower_next = lower.m_next.calc_symbolic_bit_offset (mgr);
2299 125 : const svalue &upper_start = upper.m_start.calc_symbolic_bit_offset (mgr);
2300 125 : const svalue *num_bits_gap
2301 125 : = mgr->get_or_create_binop (NULL_TREE, MINUS_EXPR,
2302 : &upper_start, &lower_next);
2303 125 : if (m_logger)
2304 0 : m_logger->log ("num_bits_gap: %qs", num_bits_gap->get_desc ().get ());
2305 :
2306 125 : const svalue *zero = mgr->get_or_create_int_cst (NULL_TREE, 0);
2307 125 : tristate ts_gt_zero = m_op.m_model.eval_condition (num_bits_gap,
2308 : GT_EXPR,
2309 : zero);
2310 125 : if (ts_gt_zero.is_false ())
2311 : {
2312 111 : if (m_logger)
2313 0 : m_logger->log ("rejecting as not > 0");
2314 111 : return;
2315 : }
2316 :
2317 14 : bit_size_expr num_bits (*num_bits_gap);
2318 14 : if (auto p = num_bits.maybe_get_formatted_str (m_sm, m_op.m_model,
2319 14 : _("%wi bit"),
2320 14 : _("%wi bits"),
2321 14 : _("%wi byte"),
2322 14 : _("%wi bytes"),
2323 14 : _("%qs bits"),
2324 14 : _("%qs bytes")))
2325 : {
2326 14 : styled_string label = std::move (*p.get ());
2327 28 : w->add_range (m_btm.get_table_x_for_range
2328 28 : (access_range (lower.m_next,
2329 : upper.m_start,
2330 14 : *mgr)),
2331 : std::move (label),
2332 : style::id_plain);
2333 14 : }
2334 125 : }
2335 :
2336 : styled_string
2337 72 : make_warning_string (styled_string &&text)
2338 : {
2339 72 : styled_string result;
2340 72 : if (!m_theme.emojis_p ())
2341 71 : return std::move (text);
2342 :
2343 1 : result.append (styled_string (0x26A0, /* U+26A0 WARNING SIGN. */
2344 1 : true));
2345 : /* U+26A0 WARNING SIGN has East_Asian_Width == Neutral, but in its
2346 : emoji variant is printed (by vte at least) with a 2nd half
2347 : overlapping the next char. Hence we add two spaces here: a space
2348 : to be covered by this overlap, plus another space of padding. */
2349 1 : result.append (styled_string (m_sm, " "));
2350 1 : result.append (std::move (text));
2351 1 : return result;
2352 72 : }
2353 :
2354 : /* Add a ruler child widet showing valid, invalid, and gaps. */
2355 68 : void add_valid_vs_invalid_ruler ()
2356 : {
2357 68 : LOG_SCOPE (m_logger);
2358 :
2359 68 : auto w = std::make_unique<x_aligned_x_ruler_widget> (*this, m_theme);
2360 :
2361 68 : access_range invalid_before_bits;
2362 68 : if (m_op.maybe_get_invalid_before_bits (&invalid_before_bits))
2363 : {
2364 15 : if (m_logger)
2365 0 : invalid_before_bits.log ("invalid_before_bits", *m_logger);
2366 15 : bit_size_expr num_before_bits
2367 15 : (invalid_before_bits.get_size (m_op.get_manager ()));
2368 15 : std::unique_ptr<styled_string> label;
2369 15 : if (m_op.m_dir == access_direction::read)
2370 8 : label = num_before_bits.maybe_get_formatted_str
2371 16 : (m_sm, m_op.m_model,
2372 8 : _("under-read of %wi bit"),
2373 8 : _("under-read of %wi bits"),
2374 8 : _("under-read of %wi byte"),
2375 8 : _("under-read of %wi bytes"),
2376 8 : _("under-read of %qs bits"),
2377 16 : _("under-read of %qs bytes"));
2378 : else
2379 7 : label = num_before_bits.maybe_get_formatted_str
2380 14 : (m_sm, m_op.m_model,
2381 7 : _("underwrite of %wi bit"),
2382 7 : _("underwrite of %wi bits"),
2383 7 : _("underwrite of %wi byte"),
2384 7 : _("underwrite of %wi bytes"),
2385 7 : _("underwrite of %qs bits"),
2386 14 : _("underwrite of %qs bytes"));
2387 15 : if (label)
2388 15 : w->add_range (m_btm.get_table_x_for_range (invalid_before_bits),
2389 30 : make_warning_string (std::move (*label)),
2390 15 : m_invalid_style_id);
2391 15 : }
2392 : else
2393 : {
2394 53 : if (m_logger)
2395 0 : m_logger->log ("no invalid_before_bits");
2396 : }
2397 :
2398 : /* It would be nice to be able to use std::optional<access_range> here,
2399 : but std::optional is C++17. */
2400 68 : bool got_valid_bits = false;
2401 68 : access_range valid_bits (m_op.get_valid_bits ());
2402 68 : bit_size_expr num_valid_bits (valid_bits.get_size (m_op.get_manager ()));
2403 68 : if (m_logger)
2404 0 : valid_bits.log ("valid_bits", *m_logger);
2405 :
2406 68 : got_valid_bits = true;
2407 68 : maybe_add_gap (w.get (), invalid_before_bits, valid_bits);
2408 :
2409 68 : std::unique_ptr<styled_string> label;
2410 68 : if (m_op.m_dir == access_direction::read)
2411 34 : label = num_valid_bits.maybe_get_formatted_str (m_sm,
2412 17 : m_op.m_model,
2413 17 : _("size: %wi bit"),
2414 17 : _("size: %wi bits"),
2415 17 : _("size: %wi byte"),
2416 17 : _("size: %wi bytes"),
2417 17 : _("size: %qs bits"),
2418 34 : _("size: %qs bytes"));
2419 : else
2420 51 : label
2421 102 : = num_valid_bits.maybe_get_formatted_str (m_sm,
2422 51 : m_op.m_model,
2423 51 : _("capacity: %wi bit"),
2424 51 : _("capacity: %wi bits"),
2425 51 : _("capacity: %wi byte"),
2426 51 : _("capacity: %wi bytes"),
2427 51 : _("capacity: %qs bits"),
2428 102 : _("capacity: %qs bytes"));
2429 68 : if (label)
2430 67 : w->add_range (m_btm.get_table_x_for_range (m_op.get_valid_bits ()),
2431 67 : std::move (*label),
2432 67 : m_valid_style_id);
2433 :
2434 68 : access_range invalid_after_bits;
2435 68 : if (m_op.maybe_get_invalid_after_bits (&invalid_after_bits))
2436 : {
2437 57 : if (got_valid_bits)
2438 57 : maybe_add_gap (w.get (), valid_bits, invalid_after_bits);
2439 :
2440 57 : if (m_logger)
2441 0 : invalid_before_bits.log ("invalid_after_bits", *m_logger);
2442 :
2443 57 : bit_size_expr num_after_bits
2444 57 : (invalid_after_bits.get_size (m_op.get_manager ()));
2445 57 : std::unique_ptr<styled_string> label;
2446 57 : if (m_op.m_dir == access_direction::read)
2447 11 : label = num_after_bits.maybe_get_formatted_str
2448 22 : (m_sm, m_op.m_model,
2449 11 : _("over-read of %wi bit"),
2450 11 : _("over-read of %wi bits"),
2451 11 : _("over-read of %wi byte"),
2452 11 : _("over-read of %wi bytes"),
2453 11 : _("over-read of %qs bits"),
2454 22 : _("over-read of %qs bytes"));
2455 : else
2456 46 : label = num_after_bits.maybe_get_formatted_str
2457 92 : (m_sm, m_op.m_model,
2458 46 : _("overflow of %wi bit"),
2459 46 : _("overflow of %wi bits"),
2460 46 : _("overflow of %wi byte"),
2461 46 : _("overflow of %wi bytes"),
2462 46 : _("overflow of %qs bits"),
2463 92 : _("overflow of %qs bytes"));
2464 57 : if (label)
2465 57 : w->add_range (m_btm.get_table_x_for_range (invalid_after_bits),
2466 114 : make_warning_string (std::move (*label)),
2467 57 : m_invalid_style_id);
2468 57 : }
2469 : else
2470 : {
2471 11 : if (m_logger)
2472 0 : m_logger->log ("no invalid_after_bits");
2473 : }
2474 :
2475 68 : add_child (std::move (w));
2476 68 : }
2477 :
2478 : /* Subroutine of calc_req_size.
2479 : Try to allocate surplus canvas width to table columns to make the
2480 : per table-column canvas widths closer to being to scale.
2481 : See e.g.:
2482 : https://en.wikipedia.org/wiki/Fair_item_allocation
2483 : https://en.wikipedia.org/wiki/Mathematics_of_apportionment
2484 : */
2485 68 : void adjust_to_scale ()
2486 : {
2487 68 : LOG_SCOPE (m_logger);
2488 68 : const unsigned num_columns = m_btm.get_num_columns ();
2489 68 : std::vector<bit_offset_t> bit_sizes (num_columns);
2490 624 : for (unsigned table_x = 0; table_x < num_columns; table_x++)
2491 : {
2492 556 : access_range range_for_column (nullptr, bit_range (0, 0));
2493 556 : if (m_btm.maybe_get_access_range_for_table_x (table_x,
2494 : &range_for_column))
2495 : {
2496 455 : bit_size_t size_in_bits;
2497 455 : if (!range_for_column.get_size_in_bits (&size_in_bits))
2498 60 : size_in_bits = BITS_PER_UNIT; // arbitrary non-zero value
2499 455 : gcc_assert (size_in_bits > 0);
2500 455 : bit_sizes[table_x] = size_in_bits;
2501 : }
2502 : else
2503 101 : bit_sizes[table_x] = 0;
2504 : }
2505 :
2506 725 : while (adjust_to_scale_once (bit_sizes))
2507 : {
2508 : }
2509 68 : }
2510 725 : bool adjust_to_scale_once (const std::vector<bit_offset_t> &bit_sizes)
2511 : {
2512 725 : LOG_SCOPE (m_logger);
2513 :
2514 725 : const unsigned num_columns = m_btm.get_num_columns ();
2515 :
2516 : /* Find the total canvas width currently required.
2517 : Require one extra canvas column for the right-hand border
2518 : of the table. */
2519 725 : int total_width = 1;
2520 4838 : for (unsigned table_x = 0; table_x < num_columns; table_x++)
2521 : {
2522 4113 : int canvas_w = m_col_widths->m_requirements[table_x];
2523 4113 : gcc_assert (canvas_w >= 0);
2524 4113 : total_width += canvas_w + 1;
2525 : }
2526 :
2527 725 : const int max_width = param_analyzer_text_art_ideal_canvas_width;
2528 725 : if (total_width >= max_width)
2529 : {
2530 68 : if (m_logger)
2531 0 : m_logger->log ("bailing out: total_width=%i ,>= max_width (%i)\n",
2532 : total_width, max_width);
2533 68 : return false;
2534 : }
2535 :
2536 657 : const int fixed_point = 1024;
2537 657 : std::vector<bit_offset_t> canvas_w_per_bit (num_columns);
2538 4214 : for (unsigned table_x = 0; table_x < num_columns; table_x++)
2539 : {
2540 3557 : bit_offset_t bit_size = bit_sizes[table_x];
2541 3557 : if (bit_size > 0)
2542 5192 : canvas_w_per_bit[table_x]
2543 2596 : = (m_col_widths->m_requirements[table_x] * fixed_point) / bit_size;
2544 : else
2545 961 : canvas_w_per_bit[table_x] = INT_MAX;
2546 : }
2547 :
2548 : /* Find the min canvas per bit, and give an extra canvas column to
2549 : the table column that has least. */
2550 657 : size_t min_idx = std::distance (canvas_w_per_bit.begin (),
2551 : std::min_element (canvas_w_per_bit.begin (),
2552 657 : canvas_w_per_bit.end ()));
2553 657 : m_col_widths->m_requirements[min_idx] += 1;
2554 657 : if (m_logger)
2555 0 : m_logger->log ("adding 1 canvas_w to column %i\n", (int)min_idx);
2556 :
2557 657 : return true; // keep going
2558 725 : }
2559 :
2560 : const access_operation &m_op;
2561 : diagnostics::paths::event_id_t m_region_creation_event_id;
2562 : style_manager &m_sm;
2563 : const theme &m_theme;
2564 : logger *m_logger;
2565 : /* In lieu of being able to throw exceptions, a flag to mark this object
2566 : as "invalid". */
2567 : bool m_invalid;
2568 :
2569 : style::id_t m_valid_style_id;
2570 : style::id_t m_invalid_style_id;
2571 :
2572 : valid_region_spatial_item m_valid_region_spatial_item;
2573 : accessed_region_spatial_item m_accessed_region_spatial_item;
2574 : std::unique_ptr<spatial_item> m_written_svalue_spatial_item;
2575 :
2576 : std::unique_ptr<boundaries> m_boundaries;
2577 :
2578 : bit_to_table_map m_btm;
2579 :
2580 : bool m_calc_req_size_called;
2581 :
2582 : /* Column widths shared by all x_aligned_table_widget,
2583 : created once we know how many columns we need. */
2584 : std::unique_ptr<table_dimension_sizes> m_col_widths;
2585 :
2586 : /* All of the child x_aligned_table_widget that share
2587 : column widths. */
2588 : std::vector<x_aligned_table_widget *> m_aligned_table_widgets;
2589 :
2590 : /* Mapping from table_x to canvas_x. */
2591 : std::vector<int> m_col_start_x;
2592 : };
2593 :
2594 : x_ruler
2595 136 : x_aligned_x_ruler_widget::make_x_ruler () const
2596 : {
2597 136 : x_ruler r (x_ruler::label_dir::BELOW);
2598 442 : for (auto& iter : m_labels)
2599 : {
2600 306 : canvas::range_t canvas_x_range
2601 306 : = m_dia_impl.get_canvas_x_range (iter.m_table_x_range);
2602 : /* Include the end-point. */
2603 306 : canvas_x_range.next++;
2604 306 : r.add_label (canvas_x_range, iter.m_text.copy (), iter.m_style_id,
2605 : x_ruler::label_kind::TEXT_WITH_BORDER);
2606 : }
2607 136 : return r;
2608 : }
2609 :
2610 : /* class direction_widget : public leaf_widget. */
2611 :
2612 : /* Paint arrows indicating the direction of the access (read vs write),
2613 : but only in the X-extent corresponding to the region that's actually
2614 : accessed. */
2615 :
2616 : void
2617 68 : direction_widget::paint_to_canvas (canvas &canvas)
2618 : {
2619 68 : const access_range accessed_bits (m_dia_impl.get_op ().get_actual_bits ());
2620 :
2621 68 : const access_range valid_bits (m_dia_impl.get_op ().get_valid_bits ());
2622 :
2623 624 : for (unsigned table_x = 0; table_x < m_btm.get_num_columns (); table_x++)
2624 : {
2625 556 : access_range column_access_range;
2626 556 : if (m_btm.maybe_get_access_range_for_table_x (table_x,
2627 : &column_access_range))
2628 : {
2629 : /* Only paint arrows in the accessed region. */
2630 455 : if (!accessed_bits.contains_p (column_access_range))
2631 175 : continue;
2632 :
2633 : /* Are we within the valid region? */
2634 280 : const bool is_valid (valid_bits.contains_p (column_access_range));
2635 280 : const style::id_t style_id
2636 280 : = m_dia_impl.get_style_id_for_validity (is_valid);
2637 280 : const canvas::range_t x_canvas_range
2638 280 : = m_dia_impl.get_canvas_x_range (table::range_t (table_x,
2639 280 : table_x + 1));
2640 280 : const int canvas_x = x_canvas_range.get_midpoint ();
2641 280 : m_dia_impl.get_theme ().paint_y_arrow
2642 280 : (canvas,
2643 : canvas_x,
2644 : canvas::range_t (get_y_range ()),
2645 280 : (m_dia_impl.get_op ().m_dir == access_direction::read
2646 280 : ? theme::y_arrow_dir::UP
2647 : : theme::y_arrow_dir::DOWN),
2648 : style_id);
2649 : }
2650 : }
2651 68 : }
2652 :
2653 : /* class access_diagram : public text_art::wrapper_widget. */
2654 :
2655 : /* To hide the implementation details, this is merely a wrapper around
2656 : an access_diagram_impl. */
2657 :
2658 72 : access_diagram::access_diagram (const access_operation &op,
2659 : diagnostics::paths::event_id_t region_creation_event_id,
2660 : style_manager &sm,
2661 : const theme &theme,
2662 72 : logger *logger)
2663 : : wrapper_widget
2664 72 : (std::make_unique <access_diagram_impl> (op,
2665 : region_creation_event_id,
2666 : sm,
2667 : theme,
2668 72 : logger))
2669 : {
2670 72 : }
2671 :
2672 : #if CHECKING_P
2673 :
2674 : namespace selftest {
2675 :
2676 : /* Implementation detail of ASSERT_EQ_TYPELESS_INTEGER. */
2677 :
2678 : static void
2679 8 : assert_eq_typeless_integer (const location &loc,
2680 : const svalue *sval,
2681 : int expected_int_val)
2682 : {
2683 8 : ASSERT_NE_AT (loc, sval, nullptr);
2684 8 : ASSERT_EQ_AT (loc, sval->get_kind (), SK_CONSTANT);
2685 8 : ASSERT_EQ_AT (loc,
2686 : wi::to_offset (sval->maybe_get_constant ()),
2687 : expected_int_val);
2688 8 : ASSERT_EQ_AT (loc, sval->get_type (), NULL_TREE);
2689 8 : }
2690 :
2691 : /* Assert that SVAL is a constant_svalue equal to EXPECTED_INT_VAL,
2692 : with NULL_TREE as its type. */
2693 :
2694 : #define ASSERT_EQ_TYPELESS_INTEGER(SVAL, EXPECTED_INT_VAL) \
2695 : SELFTEST_BEGIN_STMT \
2696 : assert_eq_typeless_integer ((SELFTEST_LOCATION), \
2697 : (SVAL), \
2698 : (EXPECTED_INT_VAL)); \
2699 : SELFTEST_END_STMT
2700 :
2701 :
2702 : /* Various tests of bit_size_expr::maybe_get_as_bytes. */
2703 :
2704 : static void
2705 4 : test_bit_size_expr_to_bytes ()
2706 : {
2707 4 : region_model_manager mgr;
2708 :
2709 : /* 40 bits: should be 5 bytes. */
2710 4 : {
2711 4 : bit_size_expr num_bits (*mgr.get_or_create_int_cst (NULL_TREE, 40));
2712 4 : const svalue *as_bytes = num_bits.maybe_get_as_bytes (mgr);
2713 4 : ASSERT_EQ_TYPELESS_INTEGER (as_bytes, 5);
2714 : }
2715 :
2716 : /* 41 bits: should not convert to bytes. */
2717 4 : {
2718 4 : bit_size_expr num_bits (*mgr.get_or_create_int_cst (NULL_TREE, 41));
2719 4 : const svalue *as_bytes = num_bits.maybe_get_as_bytes (mgr);
2720 4 : ASSERT_EQ (as_bytes, nullptr);
2721 : }
2722 :
2723 4 : tree n = build_global_decl ("n", size_type_node);
2724 :
2725 4 : const svalue *init_n
2726 4 : = mgr.get_or_create_initial_value (mgr.get_region_for_global (n));
2727 :
2728 4 : const svalue *n_times_8
2729 4 : = mgr.get_or_create_binop (NULL_TREE, MULT_EXPR,
2730 : init_n,
2731 4 : mgr.get_or_create_int_cst (NULL_TREE, 8));
2732 :
2733 : /* (n * 8) bits should be n bytes */
2734 4 : {
2735 4 : bit_size_expr num_bits (*n_times_8);
2736 4 : const svalue *as_bytes = num_bits.maybe_get_as_bytes (mgr);
2737 4 : ASSERT_EQ (as_bytes, mgr.get_or_create_cast (NULL_TREE, init_n));
2738 : }
2739 :
2740 : /* (n * 8) + 16 bits should be n + 2 bytes */
2741 4 : {
2742 4 : bit_size_expr num_bits
2743 4 : (*mgr.get_or_create_binop (NULL_TREE, PLUS_EXPR,
2744 : n_times_8,
2745 4 : mgr.get_or_create_int_cst (NULL_TREE, 16)));
2746 4 : const svalue *as_bytes = num_bits.maybe_get_as_bytes (mgr);
2747 4 : ASSERT_EQ (as_bytes->get_kind (), SK_BINOP);
2748 4 : const binop_svalue *binop = as_bytes->dyn_cast_binop_svalue ();
2749 4 : ASSERT_EQ (binop->get_op (), PLUS_EXPR);
2750 4 : ASSERT_EQ (binop->get_arg0 (), mgr.get_or_create_cast (NULL_TREE, init_n));
2751 4 : ASSERT_EQ_TYPELESS_INTEGER (binop->get_arg1 (), 2);
2752 : }
2753 4 : }
2754 :
2755 : /* Run all of the selftests within this file. */
2756 :
2757 : void
2758 4 : analyzer_access_diagram_cc_tests ()
2759 : {
2760 4 : test_bit_size_expr_to_bytes ();
2761 4 : }
2762 :
2763 : } // namespace selftest
2764 :
2765 : #endif /* CHECKING_P */
2766 :
2767 : } // namespace ana
2768 :
2769 : #endif /* #if ENABLE_ANALYZER */
|