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