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