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