public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 0/2] Fix two bugs with packed array printing
@ 2022-10-06 20:06 Tom Tromey
  2022-10-06 20:06 ` [PATCH 1/2] Fix bug in Ada packed array handling Tom Tromey
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Tom Tromey @ 2022-10-06 20:06 UTC (permalink / raw)
  To: gdb-patches

This series fixes a couple of bugs involving the printing of packed
arrays.

Regression tested on x86-64 Fedora 34.  I've also run these patches
through the internal test suite on many more targets (and in
particular for the second patch, some big-endian targets).

Tom



^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 1/2] Fix bug in Ada packed array handling
  2022-10-06 20:06 [PATCH 0/2] Fix two bugs with packed array printing Tom Tromey
@ 2022-10-06 20:06 ` Tom Tromey
  2022-10-06 20:06 ` [PATCH 2/2] Fix crash in value_print_array_elements Tom Tromey
  2022-10-21 15:40 ` [PATCH 0/2] Fix two bugs with packed array printing Tom Tromey
  2 siblings, 0 replies; 4+ messages in thread
From: Tom Tromey @ 2022-10-06 20:06 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

A user found a bug where an array of packed arrays was printed
incorrectly.  The bug here is that the packed array has a bit stride,
but the outer array does not -- and should not.  However,
update_static_array_size does not distinguish between an array of
packed arrays and a multi-dimensional packed array, and for the
latter, only the innermost array will end up with a stride.

This patch fixes the problem by adding a flag to indicate whether a
given array type is a constituent of a multi-dimensional array.
---
 gdb/dwarf2/read.c                         |  4 ++++
 gdb/gdbtypes.c                            |  1 +
 gdb/gdbtypes.h                            | 21 +++++++++++++++++++++
 gdb/testsuite/gdb.ada/packed_array.exp    |  6 ++++++
 gdb/testsuite/gdb.ada/packed_array/pa.adb | 14 ++++++++++++++
 5 files changed, 46 insertions(+)

diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 78f4cc1f60d..116fc59821a 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -15695,6 +15695,7 @@ read_array_type (struct die_info *die, struct dwarf2_cu *cu)
 	{
 	  type = create_array_type_with_stride (NULL, type, range_types[i++],
 						byte_stride_prop, bit_stride);
+	  type->set_is_multi_dimensional (true);
 	  bit_stride = 0;
 	  byte_stride_prop = nullptr;
 	}
@@ -15706,11 +15707,14 @@ read_array_type (struct die_info *die, struct dwarf2_cu *cu)
 	{
 	  type = create_array_type_with_stride (NULL, type, range_types[ndim],
 						byte_stride_prop, bit_stride);
+	  type->set_is_multi_dimensional (true);
 	  bit_stride = 0;
 	  byte_stride_prop = nullptr;
 	}
     }
 
+  /* Clear the flag on the outermost array type.  */
+  type->set_is_multi_dimensional (false);
   gdb_assert (type != element_type);
 
   /* Understand Dwarf2 support for vector types (like they occur on
diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c
index 643bb0a14a3..9ddb1f81cc1 100644
--- a/gdb/gdbtypes.c
+++ b/gdb/gdbtypes.c
@@ -1319,6 +1319,7 @@ update_static_array_size (struct type *type)
 	 wrong size when trying to find elements of the outer
 	 array.  */
       if (element_type->code () == TYPE_CODE_ARRAY
+	  && (stride != 0 || element_type->is_multi_dimensional ())
 	  && element_type->length () != 0
 	  && TYPE_FIELD_BITSIZE (element_type, 0) != 0
 	  && get_array_bounds (element_type, &low_bound, &high_bound)
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index 94d4b6684fb..f92b3894a1b 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -926,6 +926,15 @@ struct main_type
 
   unsigned int m_flag_flag_enum : 1;
 
+  /* * For TYPE_CODE_ARRAY, this is true if this type is part of a
+     multi-dimensional array.  Multi-dimensional arrays are
+     represented internally as arrays of arrays, and this flag lets
+     gdb distinguish between multiple dimensions and an ordinary array
+     of arrays.  The flag is set on each inner dimension, but not the
+     outermost dimension.  */
+
+  unsigned int m_multi_dimensional : 1;
+
   /* * A discriminant telling us which field of the type_specific
      union is being used for this type, if any.  */
 
@@ -1349,6 +1358,18 @@ struct type
     this->main_type->m_flag_flag_enum = is_flag_enum;
   }
 
+  /* True if this array type is part of a multi-dimensional array.  */
+
+  bool is_multi_dimensional () const
+  {
+    return this->main_type->m_multi_dimensional;
+  }
+
+  void set_is_multi_dimensional (bool value)
+  {
+    this->main_type->m_multi_dimensional = value;
+  }
+
   /* * Assuming that THIS is a TYPE_CODE_FIXED_POINT, return a reference
      to this type's fixed_point_info.  */
 
diff --git a/gdb/testsuite/gdb.ada/packed_array.exp b/gdb/testsuite/gdb.ada/packed_array.exp
index eb857f7e564..df34f31348a 100644
--- a/gdb/testsuite/gdb.ada/packed_array.exp
+++ b/gdb/testsuite/gdb.ada/packed_array.exp
@@ -57,4 +57,10 @@ foreach_with_prefix scenario {all minimal} {
 	    xfail $test
 	}
     }
+
+    set line "(4 => true, false, true, false, true)"
+    gdb_test "print o_var" \
+	[string_to_regexp " = ($line, $line, $line, $line)"]
+    gdb_test "print o2_var" \
+	[string_to_regexp " = ($line, $line, $line, $line)"]
 }
diff --git a/gdb/testsuite/gdb.ada/packed_array/pa.adb b/gdb/testsuite/gdb.ada/packed_array/pa.adb
index 2955f986b00..bb6a2acd388 100644
--- a/gdb/testsuite/gdb.ada/packed_array/pa.adb
+++ b/gdb/testsuite/gdb.ada/packed_array/pa.adb
@@ -27,6 +27,20 @@ procedure PA is
 
    U_Var : Unconstrained_Packed_Array (1 .. Ident (6));
 
+   -- Note that this array is not packed.
+   type Outer_Array is array (1 .. 4) of Packed_Array;
+   O_Var : Outer_Array := ((true, false, true, false, true),
+                           (true, false, true, false, true),
+                           (true, false, true, false, true),
+                           (true, false, true, false, true));
+
+   type Outer_Array2 is array (1 .. 4) of Packed_Array;
+   pragma pack (Outer_Array2);
+   O2_Var : Outer_Array2 := ((true, false, true, false, true),
+                           (true, false, true, false, true),
+                           (true, false, true, false, true),
+                           (true, false, true, false, true));
+
 begin
 
    Var := (True, False, True, False, True);
-- 
2.34.3


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 2/2] Fix crash in value_print_array_elements
  2022-10-06 20:06 [PATCH 0/2] Fix two bugs with packed array printing Tom Tromey
  2022-10-06 20:06 ` [PATCH 1/2] Fix bug in Ada packed array handling Tom Tromey
@ 2022-10-06 20:06 ` Tom Tromey
  2022-10-21 15:40 ` [PATCH 0/2] Fix two bugs with packed array printing Tom Tromey
  2 siblings, 0 replies; 4+ messages in thread
From: Tom Tromey @ 2022-10-06 20:06 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

A user noticed that gdb would crash when printing a packed array after
doing "set lang c".  Packed arrays don't exist in C, but it's
occasionally useful to print things in C mode when working in a non-C
language -- this lets you see under the hood a little bit.

The bug here is that generic value printing does not handle packed
arrays at all.  This patch fixes the bug by introducing a new function
to extract a value from a bit offset and width.

The new function includes a hack to avoid problems with some existing
test cases when using -fgnat-encodings=all.  Cleaning up this code
looked difficult, and since "all" is effectively deprecated, I thought
it made sense to simply work around the problems.
---
 gdb/testsuite/gdb.ada/packed_array.exp | 13 +++++
 gdb/valprint.c                         | 20 ++++---
 gdb/value.c                            | 79 ++++++++++++++++++++++++++
 gdb/value.h                            | 21 +++++++
 4 files changed, 126 insertions(+), 7 deletions(-)

diff --git a/gdb/testsuite/gdb.ada/packed_array.exp b/gdb/testsuite/gdb.ada/packed_array.exp
index df34f31348a..e73298ec84c 100644
--- a/gdb/testsuite/gdb.ada/packed_array.exp
+++ b/gdb/testsuite/gdb.ada/packed_array.exp
@@ -63,4 +63,17 @@ foreach_with_prefix scenario {all minimal} {
 	[string_to_regexp " = ($line, $line, $line, $line)"]
     gdb_test "print o2_var" \
 	[string_to_regexp " = ($line, $line, $line, $line)"]
+
+    # This is a regression test for a crash with
+    # -fgnat-encodings=minimal, and with 'all' the output is unusual,
+    # so restrict the test.
+    if {$scenario == "minimal"} {
+	set line "{true, false, true, false, true}"
+
+	gdb_test "set lang c" \
+	    "Warning: the current language does not match this frame."
+	gdb_test "print o2_var" \
+	    [string_to_regexp " = {$line, $line, $line, $line}"] \
+	    "print o2_var in c mode"
+    }
 }
diff --git a/gdb/valprint.c b/gdb/valprint.c
index 91a59419c4e..266a76dbdc4 100644
--- a/gdb/valprint.c
+++ b/gdb/valprint.c
@@ -1928,7 +1928,6 @@ value_print_array_elements (struct value *val, struct ui_file *stream,
   unsigned int things_printed = 0;
   unsigned len;
   struct type *elttype, *index_type;
-  unsigned eltlen;
   /* Position of the array element we are examining to see
      whether it is repeated.  */
   unsigned int rep1;
@@ -1939,7 +1938,9 @@ value_print_array_elements (struct value *val, struct ui_file *stream,
   struct type *type = check_typedef (value_type (val));
 
   elttype = type->target_type ();
-  eltlen = type_length_units (check_typedef (elttype));
+  unsigned bit_stride = type->bit_stride ();
+  if (bit_stride == 0)
+    bit_stride = 8 * check_typedef (elttype)->length ();
   index_type = type->index_type ();
   if (index_type->code () == TYPE_CODE_RANGE)
     index_type = index_type->target_type ();
@@ -1988,23 +1989,28 @@ value_print_array_elements (struct value *val, struct ui_file *stream,
       maybe_print_array_index (index_type, i + low_bound,
 			       stream, options);
 
+      struct value *element = value_from_component_bitsize (val, elttype,
+							    bit_stride * i,
+							    bit_stride);
       rep1 = i + 1;
       reps = 1;
       /* Only check for reps if repeat_count_threshold is not set to
 	 UINT_MAX (unlimited).  */
       if (options->repeat_count_threshold < UINT_MAX)
 	{
-	  while (rep1 < len
-		 && value_contents_eq (val, i * eltlen,
-				       val, rep1 * eltlen,
-				       eltlen))
+	  while (rep1 < len)
 	    {
+	      struct value *rep_elt
+		= value_from_component_bitsize (val, elttype,
+						rep1 * bit_stride,
+						bit_stride);
+	      if (!value_contents_eq (element, rep_elt))
+		break;
 	      ++reps;
 	      ++rep1;
 	    }
 	}
 
-      struct value *element = value_from_component (val, elttype, eltlen * i);
       common_val_print (element, stream, recurse + 1, options,
 			current_language);
 
diff --git a/gdb/value.c b/gdb/value.c
index 8ed941f3749..53b35af5620 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -916,6 +916,17 @@ value_contents_eq (const struct value *val1, LONGEST offset1,
 				 length * TARGET_CHAR_BIT);
 }
 
+/* See value.h.  */
+
+bool
+value_contents_eq (const struct value *val1, const struct value *val2)
+{
+  ULONGEST len1 = check_typedef (value_enclosing_type (val1))->length ();
+  ULONGEST len2 = check_typedef (value_enclosing_type (val2))->length ();
+  if (len1 != len2)
+    return false;
+  return value_contents_eq (val1, 0, val2, 0, len1);
+}
 
 /* The value-history records all the values printed by print commands
    during this session.  */
@@ -1368,6 +1379,43 @@ value_contents_copy_raw (struct value *dst, LONGEST dst_offset,
 			      bit_length);
 }
 
+/* A helper for value_from_component_bitsize that copies bits from SRC
+   to DEST.  */
+
+static void
+value_contents_copy_raw_bitwise (struct value *dst, LONGEST dst_bit_offset,
+				 struct value *src, LONGEST src_bit_offset,
+				 LONGEST bit_length)
+{
+  /* A lazy DST would make that this copy operation useless, since as
+     soon as DST's contents were un-lazied (by a later value_contents
+     call, say), the contents would be overwritten.  A lazy SRC would
+     mean we'd be copying garbage.  */
+  gdb_assert (!dst->lazy && !src->lazy);
+
+  /* The overwritten DST range gets unavailability ORed in, not
+     replaced.  Make sure to remember to implement replacing if it
+     turns out actually necessary.  */
+  LONGEST dst_offset = dst_bit_offset / TARGET_CHAR_BIT;
+  LONGEST length = bit_length / TARGET_CHAR_BIT;
+  gdb_assert (value_bytes_available (dst, dst_offset, length));
+  gdb_assert (!value_bits_any_optimized_out (dst, dst_bit_offset,
+					     bit_length));
+
+  /* Copy the data.  */
+  gdb::array_view<gdb_byte> dst_contents = value_contents_all_raw (dst);
+  gdb::array_view<const gdb_byte> src_contents = value_contents_all_raw (src);
+  copy_bitwise (dst_contents.data (), dst_bit_offset,
+		src_contents.data (), src_bit_offset,
+		bit_length,
+		type_byte_order (value_type (src)) == BFD_ENDIAN_BIG);
+
+  /* Copy the meta-data.  */
+  value_ranges_copy_adjusted (dst, dst_bit_offset,
+			      src, src_bit_offset,
+			      bit_length);
+}
+
 /* Copy LENGTH bytes of SRC value's (all) contents
    (value_contents_all) starting at SRC_OFFSET byte, into DST value's
    (all) contents, starting at DST_OFFSET.  If unavailable contents
@@ -3774,6 +3822,37 @@ value_from_component (struct value *whole, struct type *type, LONGEST offset)
   return v;
 }
 
+/* See value.h.  */
+
+struct value *
+value_from_component_bitsize (struct value *whole, struct type *type,
+			      LONGEST bit_offset, LONGEST bit_length)
+{
+  gdb_assert (!value_lazy (whole));
+
+  /* Preserve lvalue-ness if possible.  This is needed to avoid
+     array-printing failures (including crashes) when printing Ada
+     arrays in programs compiled with -fgnat-encodings=all.  */
+  if ((bit_offset % TARGET_CHAR_BIT) == 0
+      && (bit_length % TARGET_CHAR_BIT) == 0
+      && bit_length == TARGET_CHAR_BIT * type->length ())
+    return value_from_component (whole, type, bit_offset / TARGET_CHAR_BIT);
+
+  struct value *v = allocate_value (type);
+
+  LONGEST dst_offset = TARGET_CHAR_BIT * value_embedded_offset (v);
+  if (is_scalar_type (type) && type_byte_order (type) == BFD_ENDIAN_BIG)
+    dst_offset += TARGET_CHAR_BIT * type->length () - bit_length;
+
+  value_contents_copy_raw_bitwise (v, dst_offset,
+				   whole,
+				   TARGET_CHAR_BIT
+				   * value_embedded_offset (whole)
+				   + bit_offset,
+				   bit_length);
+  return v;
+}
+
 struct value *
 coerce_ref_if_computed (const struct value *arg)
 {
diff --git a/gdb/value.h b/gdb/value.h
index 52752df1f4c..57a7724388b 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -599,6 +599,12 @@ extern bool value_contents_eq (const struct value *val1, LONGEST offset1,
 			       const struct value *val2, LONGEST offset2,
 			       LONGEST length);
 
+/* An overload of value_contents_eq that compares the entirety of both
+   values.  */
+
+extern bool value_contents_eq (const struct value *val1,
+			       const struct value *val2);
+
 /* Read LENGTH addressable memory units starting at MEMADDR into BUFFER,
    which is (or will be copied to) VAL's contents buffer offset by
    BIT_OFFSET bits.  Marks value contents ranges as unavailable if
@@ -687,6 +693,21 @@ extern struct value *value_from_history_ref (const char *, const char **);
 extern struct value *value_from_component (struct value *, struct type *,
 					   LONGEST);
 
+
+/* Create a new value by extracting it from WHOLE.  TYPE is the type
+   of the new value.  BIT_OFFSET and BIT_LENGTH describe the offset
+   and field width of the value to extract from WHOLE -- BIT_LENGTH
+   may differ from TYPE's length in the case where WHOLE's type is
+   packed.
+
+   When the value does come from a non-byte-aligned offset or field
+   width, it will be marked non_lval.  */
+
+extern struct value *value_from_component_bitsize (struct value *whole,
+						   struct type *type,
+						   LONGEST bit_offset,
+						   LONGEST bit_length);
+
 extern struct value *value_at (struct type *type, CORE_ADDR addr);
 extern struct value *value_at_lazy (struct type *type, CORE_ADDR addr);
 
-- 
2.34.3


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH 0/2] Fix two bugs with packed array printing
  2022-10-06 20:06 [PATCH 0/2] Fix two bugs with packed array printing Tom Tromey
  2022-10-06 20:06 ` [PATCH 1/2] Fix bug in Ada packed array handling Tom Tromey
  2022-10-06 20:06 ` [PATCH 2/2] Fix crash in value_print_array_elements Tom Tromey
@ 2022-10-21 15:40 ` Tom Tromey
  2 siblings, 0 replies; 4+ messages in thread
From: Tom Tromey @ 2022-10-21 15:40 UTC (permalink / raw)
  To: Tom Tromey via Gdb-patches; +Cc: Tom Tromey

>>>>> "Tom" == Tom Tromey via Gdb-patches <gdb-patches@sourceware.org> writes:

Tom> This series fixes a couple of bugs involving the printing of packed
Tom> arrays.

Tom> Regression tested on x86-64 Fedora 34.  I've also run these patches
Tom> through the internal test suite on many more targets (and in
Tom> particular for the second patch, some big-endian targets).

I'm checking these in now.

Tom

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2022-10-21 15:40 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-10-06 20:06 [PATCH 0/2] Fix two bugs with packed array printing Tom Tromey
2022-10-06 20:06 ` [PATCH 1/2] Fix bug in Ada packed array handling Tom Tromey
2022-10-06 20:06 ` [PATCH 2/2] Fix crash in value_print_array_elements Tom Tromey
2022-10-21 15:40 ` [PATCH 0/2] Fix two bugs with packed array printing Tom Tromey

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).