[PATCH 2/2] gdb: ignore EVAL_AVOID_SIDE_EFFECTS for STRUCTOP_STRUCT
Andrew Burgess
andrew.burgess@embecosm.com
Mon Jan 11 13:20:59 GMT 2021
This commit is a replacement for this previously posted patch:
https://sourceware.org/pipermail/gdb-patches/2020-July/170335.html
However, the solution presented here is more aggressive than the
previous proposal.
The motivation behind the original patch can be seen in the new test,
which gives a GDB session like this:
(gdb) ptype var8
type = Type type6
PTR TO -> ( Type type2 :: ptr_1 )
PTR TO -> ( Type type2 :: ptr_2 )
End Type type6
(gdb) ptype var8%ptr_2
type = PTR TO -> ( Type type2
integer(kind=4) :: spacer
Type type1, allocatable :: t2_array(:)
End Type type2 )
(gdb) ptype var8%ptr_2%t2_array
Cannot access memory at address 0x8
(gdb)
The final access results in an expression that looks like this:
Dump of expression @ 0x51897e0, after conversion to prefix form:
Expression: `MAIN__::var8.ptr_2.t2_array'
Language fortran, 14 elements, 16 bytes each.
0 STRUCTOP_STRUCT Element name: `t2_array'
5 STRUCTOP_STRUCT Element name: `ptr_2'
10 OP_VAR_VALUE Block @0x4c53520, symbol @0x4c534a0 (var8)
GDB will first get a value for `var8`, from which it extracts a value
for the element `ptr_2`, and finally GDB dereferences the pointer and
extracts the element `t2_array`.
The problem is that when looking for the type of an expression GDB
evaluates the expression in EVAL_AVOID_SIDE_EFFECTS mode, as a result
the pointer value `ptr_2` is returned with contents set to the
constant value 0.
Normally this is fine as we only plan to look at the type being
pointed too, but for dynamic types we need the pointer value so we can
correctly fetch the dynamic properties of the value from target
memory. What ends up happening is that GDB reads the pointer value of
zero, and then uses this while evaluating the DWARF expressions. One
of these expressions requires GDB to access object address plus 8,
which leads to an attempt to read from memory address 0x8.
When considering a solution to this problem I was thinking about what
EVAL_AVOID_SIDE_EFFECTS is actually for. The motivation is to reduce
the chance that GDB asking for something like `ptype`, will have side
effect in the target. As with memory mapped devices, even just
reading memory could be enough to modify the target, then GDB is very
defensive and even when extracting a field from a struct, we have an
EVAL_AVOID_SIDE_EFFECTS case.
In the original patch I tried to keep this special case, but ignore it
when dynamic types are found. However, after more thought I believe
this is pointless. I now believe handling EVAL_AVOID_SIDE_EFFECTS
in this case should be removed, here's why:
The bottom layer of the expression tree OP_VAR_VALUE already chooses
to NOT handle the EVAL_AVOID_SIDE_EFFECTS. In this case it is for
'set print object' handling, however, the reasoning could just as
easily apply for dynamic type handling.
What this means is that the OP_VAR_VALUE already returns a lazy value
pointing at the actual variable contents.
Note however, that this is a lazy value. If all we do with this is
ask GDB about the values type, and the value does not have dynamic
type, the GDB will never actually fetch the value contents from the
target.
Extracting a field from a struct (the STRUCTOP_STRUCT handling), will
itself return a lazy value if we are dealing with a non-dynamically
typed field. Again, if all we do is ask about the type of this field
then GDB will never actually fetch the field value from the inferior.
It is only when GDB "needs" the field contents in order to resolve a
dynamic type that we end up reading things from the inferior.
The question then becomes which is better in the above case, having
GDB show the accurate (resolved) dynamic type, showing the unresolved
dynamic type, or maybe just throwing a more informative error?
I believe that showing the resolved dynamic type is the best solution,
and I believe that this is what other languages with dynamic types,
like Ada, already do. I think when debugging, the user is more likely
to want to know the resolved type than the unresolved type.
In order to prove to myself that making the change I propose in this
patch does not cause GDB to access any additional target memory I did
the following:
Modified STRUCTOP_STRUCT handling to make the EVAL_AVOID_SIDE_EFFECTS
block switchable, like:
if (noside == EVAL_AVOID_SIDE_EFFECTS && gdb_old_structop_struct)
arg3 = value_zero (value_type (arg3), VALUE_LVAL (arg3));
Then I created a new record_stratum target which implemented the
xfer_partial method only. In the xfer_partial I recorded into a list
all of the memory accesses that were performed.
Next I modified evaluate_type (the entry point for expression
evaluation for things like ptype and whatis) to do the following:
- Clear the dcache,
- Push the new record_stratum target,
- Set gdb_old_structop_struct to true,
- Call evaluate_subexp,
- Take a copy of all memory accesses performed.
- Now clear the list of all memory accesses in the record_stratum
target,
- Set gdb_old_structop_struct to false,
- Call evaluate_subexp again,
- Compare the list of memory access between the old way and the new
way.
- If we did more memory accesses when using the new way than when
using the old way, then print a message.
I then re-ran the testsuite and looked to see how many times the
message was printed.
The only times I saw the message being printed was for the new Fortran
tests that I added in this commit - this was expected, as these tests
are the ones that print the type for dynamic fields.
For me this confirms that my reasoning behind this patch is sound and
that it is safe for use to remove the EVAL_AVOID_SIDE_EFFECTS handling
from STRUCTOP_STRUCT and GDB will not access target memory any more
than it currently does.
In this commit I am only proposing that we modify the STRUCTOP_STRUCT
handling, though I think that we could (and possibly should) update
others like STRUCTOP_PTR too. However, Fortran doesn't make use of
STRUCTOP_PTR so I don't have any actual need to change how this is
handled.
All feedback is welcome.
gdb/ChangeLog:
* eval.c (evaluate_subexp_standard): Remove
EVAL_AVOID_SIDE_EFFECTS handling from STRUCTOP_STRUCT.
gdb/testsuite/ChangeLog:
* gdb.fortran/dynamic-ptype-whatis.exp: New file.
* gdb.fortran/dynamic-ptype-whatis.f90: New file.
---
gdb/ChangeLog | 5 +
gdb/eval.c | 2 -
gdb/testsuite/ChangeLog | 5 +
.../gdb.fortran/dynamic-ptype-whatis.exp | 158 ++++++++++++++++++
.../gdb.fortran/dynamic-ptype-whatis.f90 | 93 +++++++++++
5 files changed, 261 insertions(+), 2 deletions(-)
create mode 100644 gdb/testsuite/gdb.fortran/dynamic-ptype-whatis.exp
create mode 100644 gdb/testsuite/gdb.fortran/dynamic-ptype-whatis.f90
diff --git a/gdb/eval.c b/gdb/eval.c
index dfe6e403f97..0a2ae8baf28 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -1837,8 +1837,6 @@ evaluate_subexp_standard (struct type *expect_type,
return eval_skip_value (exp);
arg3 = value_struct_elt (&arg1, NULL, &exp->elts[pc + 2].string,
NULL, "structure");
- if (noside == EVAL_AVOID_SIDE_EFFECTS)
- arg3 = value_zero (value_type (arg3), VALUE_LVAL (arg3));
return arg3;
case STRUCTOP_PTR:
diff --git a/gdb/testsuite/gdb.fortran/dynamic-ptype-whatis.exp b/gdb/testsuite/gdb.fortran/dynamic-ptype-whatis.exp
new file mode 100644
index 00000000000..d2ffd6d73f7
--- /dev/null
+++ b/gdb/testsuite/gdb.fortran/dynamic-ptype-whatis.exp
@@ -0,0 +1,158 @@
+# Copyright 2021 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/> .
+
+# Test using whatis and ptype on different configurations of dynamic
+# types.
+
+if {[skip_fortran_tests]} { return -1 }
+
+standard_testfile ".f90"
+load_lib fortran.exp
+
+if {[prepare_for_testing ${testfile}.exp ${testfile} ${srcfile} \
+ {debug f90}]} {
+ return -1
+}
+
+if {![fortran_runto_main]} {
+ perror "Could not run to main."
+ continue
+}
+
+gdb_breakpoint [gdb_get_line_number "Break Here"]
+gdb_continue_to_breakpoint "Break Here"
+
+gdb_test "whatis var1" "type = real\\(kind=4\\) \\(3\\)"
+gdb_test "whatis var2" "type = real\\(kind=4\\), allocatable \\(4\\)"
+gdb_test "whatis var3" "type = Type type1"
+gdb_test "whatis var4" "type = Type type2"
+gdb_test "whatis var5" "type = Type type3"
+gdb_test "whatis var6" "type = Type type4"
+gdb_test "whatis var7" "type = Type type5"
+gdb_test "ptype var1" "type = real\\(kind=4\\) \\(3\\)"
+gdb_test "ptype var2" "type = real\\(kind=4\\), allocatable \\(4\\)"
+gdb_test "ptype var3" \
+ [ multi_line "type = Type type1" \
+ " integer\\(kind=4\\) :: spacer" \
+ " integer\\(kind=4\\) :: t1_i" \
+ "End Type type1" ]
+gdb_test "ptype var4" \
+ [multi_line "type = Type type2" \
+ " integer\\(kind=4\\) :: spacer" \
+ " Type type1, allocatable :: t2_array\\(3\\)" \
+ "End Type type2"]
+gdb_test "ptype var5" \
+ [ multi_line "type = Type type3" \
+ " integer\\(kind=4\\) :: spacer" \
+ " Type type1 :: t3_array\\(3\\)"\
+ "End Type type3" ]
+gdb_test "ptype var6" \
+ [ multi_line "type = Type type4" \
+ " integer\\(kind=4\\) :: spacer" \
+ " Type type2, allocatable :: t4_array\\(3\\)" \
+ "End Type type4" ]
+gdb_test "ptype var7" \
+ [ multi_line "type = Type type5" \
+ " integer\\(kind=4\\) :: spacer" \
+ " Type type2 :: t5_array\\(4\\)" \
+ "End Type type5" ]
+gdb_test "whatis var3%t1_i" "type = integer\\(kind=4\\)"
+gdb_test "whatis var4%t2_array" "type = Type type1, allocatable \\(3\\)"
+gdb_test "whatis var5%t3_array" "type = Type type1 \\(3\\)"
+gdb_test "whatis var6%t4_array" "type = Type type2, allocatable \\(3\\)"
+gdb_test "whatis var7%t5_array" "type = Type type2 \\(4\\)"
+gdb_test "ptype var3%t1_i" [ multi_line "type = integer\\(kind=4\\)" ]
+gdb_test "ptype var4%t2_array" [ multi_line "type = Type type1" \
+ " integer\\(kind=4\\) :: spacer" \
+ " integer\\(kind=4\\) :: t1_i" \
+ "End Type type1, allocatable \\(3\\)" ]
+gdb_test "ptype var5%t3_array" [ multi_line "type = Type type1" \
+ " integer\\(kind=4\\) :: spacer" \
+ " integer\\(kind=4\\) :: t1_i" \
+ "End Type type1 \\(3\\)" ]
+gdb_test "ptype var6%t4_array" \
+ [ multi_line "type = Type type2" \
+ " integer\\(kind=4\\) :: spacer" \
+ " Type type1, allocatable :: t2_array\\(:\\)" \
+ "End Type type2, allocatable \\(3\\)" ]
+gdb_test "ptype var7%t5_array" \
+ [ multi_line "type = Type type2" \
+ " integer\\(kind=4\\) :: spacer" \
+ " Type type1, allocatable :: t2_array\\(:\\)" \
+ "End Type type2 \\(4\\)" ]
+gdb_test "whatis var4%t2_array(1)" "type = Type type1"
+gdb_test "whatis var5%t3_array(1)" "type = Type type1"
+gdb_test "whatis var6%t4_array(1)" "type = Type type2"
+gdb_test "whatis var7%t5_array(1)" "type = Type type2"
+gdb_test "ptype var4%t2_array(1)" \
+ [ multi_line "type = Type type1" \
+ " integer\\(kind=4\\) :: spacer" \
+ " integer\\(kind=4\\) :: t1_i" \
+ "End Type type1" ]
+gdb_test "ptype var5%t3_array(1)" \
+ [ multi_line "type = Type type1" \
+ " integer\\(kind=4\\) :: spacer" \
+ " integer\\(kind=4\\) :: t1_i" \
+ "End Type type1" ]
+gdb_test "ptype var6%t4_array(1)" \
+ [ multi_line "type = Type type2" \
+ " integer\\(kind=4\\) :: spacer" \
+ " Type type1, allocatable :: t2_array\\(2\\)" \
+ "End Type type2" ]
+gdb_test "ptype var7%t5_array(1)" \
+ [ multi_line "type = Type type2" \
+ " integer\\(kind=4\\) :: spacer" \
+ " Type type1, allocatable :: t2_array\\(2\\)" \
+ "End Type type2" ]
+gdb_test "whatis var4%t2_array(1)%t1_i" "type = integer\\(kind=4\\)"
+gdb_test "whatis var5%t3_array(1)%t1_i" "type = integer\\(kind=4\\)"
+gdb_test "whatis var6%t4_array(1)%t2_array" \
+ "type = Type type1, allocatable \\(2\\)"
+gdb_test "whatis var7%t5_array(1)%t2_array" \
+ "type = Type type1, allocatable \\(2\\)"
+gdb_test "ptype var4%t2_array(1)%t1_i" "type = integer\\(kind=4\\)"
+gdb_test "ptype var5%t3_array(1)%t1_i" "type = integer\\(kind=4\\)"
+gdb_test "ptype var6%t4_array(1)%t2_array" \
+ [ multi_line "type = Type type1" \
+ " integer\\(kind=4\\) :: spacer" \
+ " integer\\(kind=4\\) :: t1_i" \
+ "End Type type1, allocatable \\(2\\)" ]
+gdb_test "ptype var7%t5_array(1)%t2_array" \
+ [ multi_line "type = Type type1" \
+ " integer\\(kind=4\\) :: spacer" \
+ " integer\\(kind=4\\) :: t1_i" \
+ "End Type type1, allocatable \\(2\\)" ]
+gdb_test "whatis var6%t4_array(1)%t2_array(1)" \
+ "type = Type type1"
+gdb_test "whatis var7%t5_array(1)%t2_array(1)" \
+ "type = Type type1"
+gdb_test "ptype var6%t4_array(1)%t2_array(1)" \
+ [ multi_line \
+ "type = Type type1" \
+ " integer\\(kind=4\\) :: spacer" \
+ " integer\\(kind=4\\) :: t1_i" \
+ "End Type type1" ]
+gdb_test "ptype var7%t5_array(1)%t2_array(1)" \
+ [ multi_line \
+ "type = Type type1" \
+ " integer\\(kind=4\\) :: spacer" \
+ " integer\\(kind=4\\) :: t1_i" \
+ "End Type type1" ]
+gdb_test "ptype var8%ptr_1%t2_array" \
+ [ multi_line \
+ "type = Type type1" \
+ " integer\\(kind=4\\) :: spacer" \
+ " integer\\(kind=4\\) :: t1_i" \
+ "End Type type1, allocatable \\(3\\)" ]
diff --git a/gdb/testsuite/gdb.fortran/dynamic-ptype-whatis.f90 b/gdb/testsuite/gdb.fortran/dynamic-ptype-whatis.f90
new file mode 100644
index 00000000000..e56bf7952dc
--- /dev/null
+++ b/gdb/testsuite/gdb.fortran/dynamic-ptype-whatis.f90
@@ -0,0 +1,93 @@
+! Copyright 2021 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/>.
+
+program main
+
+ ! A non-dynamic type.
+ type type1
+ integer(kind=4) :: spacer
+ integer(kind=4) t1_i
+ end type type1
+
+ ! A first dynamic type. The array is of a static type.
+ type type2
+ integer(kind=4) :: spacer
+ type(type1), allocatable :: t2_array(:)
+ end type type2
+
+ ! Another dynamic type, the array is again a static type.
+ type type3
+ integer(kind=4) :: spacer
+ type(type1), pointer :: t3_array(:)
+ end type type3
+
+ ! A dynamic type, this time the array contains a dynamic type.
+ type type4
+ integer(kind=4) :: spacer
+ type(type2), allocatable :: t4_array(:)
+ end type type4
+
+ ! A static type, the array though contains dynamic types.
+ type type5
+ integer(kind=4) :: spacer
+ type(type2) :: t5_array (4)
+ end type type5
+
+ ! A static type containing pointers to a type that contains a
+ ! dynamic array.
+ type type6
+ type(type2), pointer :: ptr_1
+ type(type2), pointer :: ptr_2
+ end type type6
+
+ real, dimension(:), pointer :: var1
+ real, dimension(:), allocatable :: var2
+ type(type1) :: var3
+ type(type2), target :: var4
+ type(type3) :: var5
+ type(type4) :: var6
+ type(type5) :: var7
+ type(type6) :: var8
+
+ allocate (var1 (3))
+
+ allocate (var2 (4))
+
+ allocate (var4%t2_array(3))
+
+ allocate (var5%t3_array(3))
+
+ allocate (var6%t4_array(3))
+ allocate (var6%t4_array(1)%t2_array(2))
+ allocate (var6%t4_array(2)%t2_array(5))
+ allocate (var6%t4_array(3)%t2_array(4))
+
+ allocate (var7%t5_array(1)%t2_array(2))
+ allocate (var7%t5_array(2)%t2_array(5))
+ allocate (var7%t5_array(3)%t2_array(4))
+ allocate (var7%t5_array(4)%t2_array(1))
+
+ var8%ptr_1 => var4
+ var8%ptr_2 => var4
+
+ print *, var1 ! Break Here
+ print *, var2
+ print *, var3
+ print *, var4%t2_array(1)
+ print *, var5%t3_array(2)
+ print *, var6%t4_array(1)%t2_array(1)
+ print *, var7%t5_array(1)%t2_array(1)
+
+end program main
--
2.25.4
More information about the Gdb-patches
mailing list