[FYI/Ada/pushed] improved error message when getting an exception printing a variable

Joel Brobecker brobecker@adacore.com
Mon Dec 18 03:41:00 GMT 2017


Hello,

Consider the following Ada code defining a global variable whose
type is an array of static bounds (1 .. 2), but where its elements
are a variant record whose size is not statically known:

    type Ints is array (Natural range <>) of Integer;
    type Bounded_Ints (Max_Size : Natural) is record
       Length : Natural := 0;
       Objs   : Ints (1 .. Max_Size);
    end record;

    type Ints_Doubled is array (1 .. 2) of Bounded_Ints (Idem (0));

    Global : Ints_Doubled;

When compiling this program at -O2 using a GCC-6.4-based compiler
on x86_64-linux, trying to print the value of that global variable
yields:

    (gdb) p global
    $1 =

Let's look at the debugging info, which starts with the global
variable itself...

        .uleb128 0x19   # (DIE (0x25e) DW_TAG_variable)
        .long   .LASF32 # DW_AT_name: "fd__global"
        .long   0x273   # DW_AT_type

... its type is a reference to a typedef ...

        .uleb128 0x14   # (DIE (0x273) DW_TAG_reference_type)
        .byte   0x8     # DW_AT_byte_size
        .long   0x202   # DW_AT_type
        [...]
        .uleb128 0x15   # (DIE (0x202) DW_TAG_typedef)
        .long   .LASF19 # DW_AT_name: "fd__ints_doubled"
        .long   0x20d   # DW_AT_type

... of an array (1..2) ...

        .uleb128 0x2    # (DIE (0x20d) DW_TAG_array_type)
        .long   .LASF19 # DW_AT_name: "fd__ints_doubled"
        .long   0x15b   # DW_AT_type
        .long   0x221   # DW_AT_sibling
        .uleb128 0x16   # (DIE (0x21a) DW_TAG_subrange_type)
        .long   0x40    # DW_AT_type
        .sleb128 2      # DW_AT_upper_bound
        .byte   0       # end of children of DIE 0x20d

... of a struct whose name is fd__Tints_doubledC:

        .uleb128 0x10   # (DIE (0x15b) DW_TAG_structure_type)
        .long   .LASF11 # DW_AT_name: "fd__Tints_doubledC"
        .long   0x1e4   # DW_AT_GNAT_descriptive_type
                        # DW_AT_artificial
        .long   0x1e4   # DW_AT_sibling
        .uleb128 0x7    # (DIE (0x16a) DW_TAG_member)
        .long   .LASF4  # DW_AT_name: "max_size"
        [snip]

The error occurs while Ada evaluator is trying to "fix"
the element type inside the array, so as to determine its actual
size. For that, it searches for a parallel "XVZ" variable,
which, when found, contains the object's actual size.

Unfortunately in our case, the variable exists but has been
optimized out, as seen by the presence of a variable DIE in
the debugging info, but with no address attribute:

        .uleb128 0x18   # (DIE (0x24e) DW_TAG_variable)
        .long   .LASF31 # DW_AT_name: "fd__Tints_doubledC___XVZ"
        .long   0x257   # DW_AT_type
                        # DW_AT_artificial

Discussing this with some members of AdaCore's compiler team,
it is expected that the optimizer can get rid of this variable,
and we don't want to pessimize the code just to improve debuggability,
since -O2 is about performance. So, the idea of this patch is
not to make it work, but provide a bit more information to help
users understand what kind of error is preventing GDB from being
able to print the variable's value.

The first hurdle we had to clear was the fact that ada_val_print
traps all exceptions (including QUIT ones!), and does so completly
silently. So, the fix was to add a trace of the exception being
generated. While doing so, we fix an old XXX/FIXME by only catching
errors, letting QUIT exceptions go through.

Once this is done, we now get an error message, which gives a first
clue as to what was happening:

    (gdb) p fd.global
    $1 = <error reading variable: value has been optimized out>

However, it would be more useful to know which value it was
that was optimized out. For that purpose, we enhanced
ada-lang.c::ada_to_fixed_type_1 so as to re-throw the error
with a message which indicates which variable we failed to read.

With those changes, the new output is now:

    (gdb) p fd.global
    $1 = <error reading variable: unable to read value of fd__Tints_doubledC___XVZ (value has been optimized out)>

gdb/ChangeLog:

        * ada-lang.c (ada_to_fixed_type_1): Rethrow errors with
        a more detailed exception message when getting an exception
        while trying to read the value of an XVZ variable.
        * ada-valprint.c (ada_val_print): Only catch RETURN_MASK_ERROR
        exceptions.  Print an error message when an exception is caught.

gdb/testsuite/ChangeLog:

        * gdb.dwarf2/ada-valprint-error.c: New file.
        * gdb.dwarf2/ada-valprint-error.exp: New file.

Tested on x86_64-linux and pushed to master.

Thanks,
-- 
Joel

---
 gdb/ChangeLog                                   |   8 ++
 gdb/ada-lang.c                                  |  21 ++++-
 gdb/ada-valprint.c                              |   6 +-
 gdb/testsuite/ChangeLog                         |   5 ++
 gdb/testsuite/gdb.dwarf2/ada-valprint-error.c   |  23 +++++
 gdb/testsuite/gdb.dwarf2/ada-valprint-error.exp | 115 ++++++++++++++++++++++++
 6 files changed, 173 insertions(+), 5 deletions(-)
 create mode 100644 gdb/testsuite/gdb.dwarf2/ada-valprint-error.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/ada-valprint-error.exp

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 5e65e5f..4023472 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,13 @@
 2017-12-18  Joel Brobecker  <brobecker@adacore.com>
 
+	* ada-lang.c (ada_to_fixed_type_1): Rethrow errors with
+	a more detailed exception message when getting an exception
+	while trying to read the value of an XVZ variable.
+	* ada-valprint.c (ada_val_print): Only catch RETURN_MASK_ERROR
+	exceptions.  Print an error message when an exception is caught.
+
+2017-12-18  Joel Brobecker  <brobecker@adacore.com>
+
 	* ada-lang.c (assign_component): Strip any TYPE_CODE_TYPEDEF
 	layer from lhs' type.
 	(assign_aggregate): Likewise.
diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 14a0bd6..dad2b17 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -9101,11 +9101,28 @@ ada_to_fixed_type_1 (struct type *type, const gdb_byte *valaddr,
             const char *name = ada_type_name (fixed_record_type);
             char *xvz_name
 	      = (char *) alloca (strlen (name) + 7 /* "___XVZ\0" */);
+	    bool xvz_found = false;
             LONGEST size;
 
             xsnprintf (xvz_name, strlen (name) + 7, "%s___XVZ", name);
-            if (get_int_var_value (xvz_name, size)
-		&& TYPE_LENGTH (fixed_record_type) != size)
+	    TRY
+	      {
+		xvz_found = get_int_var_value (xvz_name, size);
+	      }
+	    CATCH (except, RETURN_MASK_ERROR)
+	      {
+		/* We found the variable, but somehow failed to read
+		   its value.  Rethrow the same error, but with a little
+		   bit more information, to help the user understand
+		   what went wrong (Eg: the variable might have been
+		   optimized out).  */
+		throw_error (except.error,
+			     _("unable to read value of %s (%s)"),
+			     xvz_name, except.message);
+	      }
+	    END_CATCH
+
+            if (xvz_found && TYPE_LENGTH (fixed_record_type) != size)
               {
                 fixed_record_type = copy_type (fixed_record_type);
                 TYPE_LENGTH (fixed_record_type) = size;
diff --git a/gdb/ada-valprint.c b/gdb/ada-valprint.c
index 91dc301..b4a48ce 100644
--- a/gdb/ada-valprint.c
+++ b/gdb/ada-valprint.c
@@ -1168,16 +1168,16 @@ ada_val_print (struct type *type,
 	       struct value *val,
 	       const struct value_print_options *options)
 {
-
-  /* XXX: this catches QUIT/ctrl-c as well.  Isn't that busted?  */
   TRY
     {
       ada_val_print_1 (type, embedded_offset, address,
 		       stream, recurse, val, options,
 		       current_language);
     }
-  CATCH (except, RETURN_MASK_ALL)
+  CATCH (except, RETURN_MASK_ERROR)
     {
+      fprintf_filtered (stream, _("<error reading variable: %s>"),
+			except.message);
     }
   END_CATCH
 }
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index c074186..51c9864 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,5 +1,10 @@
 2017-12-18  Joel Brobecker  <brobecker@adacore.com>
 
+	* gdb.dwarf2/ada-valprint-error.c: New file.
+	* gdb.dwarf2/ada-valprint-error.exp: New file.
+
+2017-12-18  Joel Brobecker  <brobecker@adacore.com>
+
 	* gdb.ada/assign_arr: New testcase.
 
 2017-12-18  Xavier Roirand  <roirand@adacore.com>
diff --git a/gdb/testsuite/gdb.dwarf2/ada-valprint-error.c b/gdb/testsuite/gdb.dwarf2/ada-valprint-error.c
new file mode 100644
index 0000000..4f04ab7
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/ada-valprint-error.c
@@ -0,0 +1,23 @@
+/* Copyright 2017 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int buffer[] = {0, 0};
+void *fd__global = &buffer;
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.dwarf2/ada-valprint-error.exp b/gdb/testsuite/gdb.dwarf2/ada-valprint-error.exp
new file mode 100644
index 0000000..192b0cb
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/ada-valprint-error.exp
@@ -0,0 +1,115 @@
+# Copyright 2017 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+standard_testfile ada-valprint-error.c ada-valprint-error-dw.S
+
+# We need to know the size of integer and address types in order
+# to write some of the debugging info we'd like to generate.
+#
+# For that, we ask GDB by debugging our ada-valprint-error.c program.
+# Any program would do, but since we already have ada-valprint-error.c
+# specifically for this testcase, might as well use that.
+
+if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
+    untested ${testfile}.exp
+    return -1
+}
+
+# Make some DWARF for the test.
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    cu {} {
+ 	DW_TAG_compile_unit {
+                {DW_AT_language @DW_LANG_Ada95}
+                {DW_AT_name     fd.adb}
+                {DW_AT_comp_dir /tmp}
+        } {
+	    declare_labels array_elt_label integer_label array_label \
+                typedef_label ref_type_label
+            set ptr_size [get_sizeof "void *" 96]
+            set int_size [get_sizeof "int" 4]
+
+            # A structure with no size attribute, to mimick structures
+            # in Ada that have a dynamic size...
+            array_elt_label: structure_type {
+                {name fd__Tints_doubledC}
+                {artificial 1 DW_FORM_flag_present}
+            }
+
+            # ... and a corresponding XVZ variable, supposed to be there
+            # to provide the actual size.  Except that, in this case,
+            # the variable has no address, to simulate the fact that
+            # it has been optimized out (which the compiler can do,
+            # even if it at the expense of debuggability).
+            DW_TAG_variable {
+                {name fd__Tints_doubledC___XVZ}
+                {DW_AT_type :$integer_label}
+                {artificial 1 DW_FORM_flag_present}
+            }
+
+            integer_label: DW_TAG_base_type {
+                {DW_AT_byte_size $int_size DW_FORM_sdata}
+                {DW_AT_encoding  @DW_ATE_signed}
+                {DW_AT_name      integer}
+            }
+
+	    array_label: DW_TAG_array_type {
+		{DW_AT_name fd__ints_doubled}
+		{DW_AT_type :$array_elt_label}
+	    } {
+		DW_TAG_subrange_type {
+		    {DW_AT_type        :$integer_label}
+		    {DW_AT_upper_bound 2 DW_FORM_data1}
+		}
+	    }
+
+            typedef_label: DW_TAG_typedef {
+                {DW_AT_name fd__ints_doubled}
+                {DW_AT_type :$array_label}
+            }
+
+            ref_type_label: DW_TAG_reference_type {
+                {DW_AT_byte_size $ptr_size DW_FORM_sdata}
+                {DW_AT_type :$typedef_label}
+            }
+
+            DW_TAG_variable {
+                {name fd__global}
+                {DW_AT_type :$ref_type_label}
+                {DW_AT_location {
+                    DW_OP_addr [gdb_target_symbol fd__global]
+                } SPECIAL_expr}
+                {external 1 flag}
+            }
+
+	}
+    }
+}
+
+if { [prepare_for_testing ${testfile}.exp ${testfile} \
+	  [list $srcfile $asm_file] {nodebug}] } {
+    return -1
+}
+
+gdb_test_no_output "set language ada"
+
+gdb_test "print fd.global" \
+         " = <error reading variable: unable to read value of fd__Tints_doubledC___XVZ \\(value has been optimized out\\)>"
-- 
2.1.4



More information about the Gdb-patches mailing list