[PATCH] gdb: user variables with components of dynamic type
Andrew Burgess
andrew.burgess@embecosm.com
Fri Nov 6 23:04:22 GMT 2020
Ping!
Any thoughts?
Thanks,
Andrew
* Andrew Burgess <andrew.burgess@embecosm.com> [2020-10-22 16:32:38 +0100]:
> Consider this Fortran type:
>
> type :: some_type
> integer, allocatable :: array_one (:,:)
> integer :: a_field
> integer, allocatable :: array_two (:,:)
> end type some_type
>
> And a variable declared:
>
> type(some_type) :: some_var
>
> Now within GDB we try this:
>
> (gdb) set $a = some_var
> (gdb) p $a
> $1 = ( array_one =
> ../../src/gdb/value.c:3968: internal-error: Unexpected lazy value type.
>
> Normally, when an internalvar ($a in this case) is created, it is
> non-lazy, the value is immediately copied out of the inferior into
> GDB's memory.
>
> When printing the internalvar ($a) GDB will extract each field in
> turn, so in this case `array_one`. As the original internalvar is
> non-lazy then the extracted field will also be non-lazy, with its
> contents immediately copied from the parent internalvar.
>
> However, when the field has a dynamic type this is not the case,
> value_primitive_field we see that any field with dynamic type is
> always created lazy. Further, the content of this field will usually
> not have been captured in the contents buffer of the original value, a
> field with dynamic location is effectively a pointer value contained
> within the parent value, with rules in the DWARF for how to
> dereference the pointer.
>
> So, we end up with a lazy lval_internalvar_component representing a
> field within an lval_internalvar. This eventually ends up in
> value_fetch_lazy, which currently does not support
> lval_internalvar_component, and we see the error above.
>
> My original plan for how to handle this involved extending
> value_fetch_lazy to handle lval_internalvar_component. However, when
> I did this I ran into another error:
>
> (gdb) set $a = some_var
> (gdb) p $a
> $1 = ( array_one = ((1, 1) (1, 1) (1, 1)), a_field = 5, array_two = ((0, 0, 0) (0, 0, 0)) )
> (gdb) p $a%array_one
> $2 = ((1, 1) (1, 1) (1, 1))
> (gdb) p $a%array_one(1,1)
> ../../src/gdb/value.c:1547: internal-error: void set_value_address(value*, CORE_ADDR): Assertion `value->lval == lval_memory' failed.
>
> The problem now is some patch up code inside
> set_value_component_location, where we attempt to set the address for
> a component, if the original parent value has a dynamic location. GDB
> does not expect to ever set the address on anything other than an
> lval_memory value (which seems reasonable).
>
> In order to resolve this issue I initially thought about how an
> internalvar should "capture" the value of a program variable at the
> moment the var is created. In an ideal world (I think) GDB would be
> able to do this even for values with dynamic type. So in our above
> example doing `set $a = some_var` would capture the content of
> 'some_var', but also the content of 'array_one', and also 'array_two',
> even these content regions are not contained within the region of
> 'some_var'.
>
> Supporting this would require GDB values to be able to carry around
> multiple non-contiguous regions of memory at content in some way,
> which sounds like a pretty huge change to a core part of GDB.
>
> So, I wondered if there was some other solution that wouldn't require
> such a huge change.
>
> What if values with a dynamic location were though of like points with
> automatic dereferencing? Given this C structure:
>
> struct foo_t {
> int *val;
> }
>
> struct foo_t my_foo;
>
> Then in GDB:
>
> (gdb) $a = my_foo
>
> We would expect GDB to capture the pointer value in '$a', but not the
> value pointed at by the pointer. So maybe it's not that unreasonable
> to think that given a dynamically typed field GDB will capture the
> address of the content, but not the actual content itself.
>
> That's what this patch does.
>
> The approach is to catch this case in set_value_component_location,
> when we create a component location (of an lval_internalvar) that has
> a dynamic data location, the lval_internalvar_component is changed
> into an lval_memory. After this both of the above issues are
> resolved. In the first case, the lval_memory is still lazy, but
> value_fetch_lazy knows how to handle that. In the second case, when
> we access the element of the array we are now accessing an element of
> an lval_memory, not an lval_internalvar_component, and calling
> set_value_address on an lval_memory is fine.
>
> gdb/ChangeLog:
>
> * value.c (set_value_component_location): Adjust the VALUE_LVAL
> for internalvar components that have a dynamic location.
>
> gdb/testsuite/ChangeLog:
>
> * gdb.fortran/intvar-dynamic-types.exp: New file.
> * gdb.fortran/intvar-dynamic-types.f90: New file.
> ---
> gdb/ChangeLog | 5 +
> gdb/testsuite/ChangeLog | 5 +
> .../gdb.fortran/intvar-dynamic-types.exp | 97 +++++++++++++++++++
> .../gdb.fortran/intvar-dynamic-types.f90 | 32 ++++++
> gdb/value.c | 29 +++++-
> 5 files changed, 166 insertions(+), 2 deletions(-)
> create mode 100644 gdb/testsuite/gdb.fortran/intvar-dynamic-types.exp
> create mode 100644 gdb/testsuite/gdb.fortran/intvar-dynamic-types.f90
>
> diff --git a/gdb/testsuite/gdb.fortran/intvar-dynamic-types.exp b/gdb/testsuite/gdb.fortran/intvar-dynamic-types.exp
> new file mode 100644
> index 00000000000..9cf5cee02ff
> --- /dev/null
> +++ b/gdb/testsuite/gdb.fortran/intvar-dynamic-types.exp
> @@ -0,0 +1,97 @@
> +# Copyright 2020 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/>.
> +
> +# Places a value with components that have dynamic type into a GDB
> +# user variable, and then prints the user variable.
> +
> +standard_testfile ".f90"
> +load_lib "fortran.exp"
> +
> +if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile} \
> + {debug f90 quiet}] } {
> + return -1
> +}
> +
> +if ![fortran_runto_main] {
> + untested "could not run to main"
> + return -1
> +}
> +
> +gdb_breakpoint [gdb_get_line_number "Break here"]
> +gdb_continue_to_breakpoint "Break here"
> +
> +gdb_test_no_output "set \$a=some_var" "set \$a internal variable"
> +
> +foreach var { "\$a" "some_var" } {
> + with_test_prefix "print $var" {
> + gdb_test "print $var" \
> + " = \\( array_one = \\(\\(1, 1\\) \\(1, 1\\) \\(1, 1\\)\\), a_field = 5, array_two = \\(\\(2, 2, 2\\) \\(2, 2, 2\\)\\) \\)" \
> + "print full contents"
> +
> + gdb_test "print $var%array_one" \
> + " = \\(\\(1, 1\\) \\(1, 1\\) \\(1, 1\\)\\)" \
> + "print array_one field"
> +
> + gdb_test "print $var%a_field" \
> + " = 5" \
> + "print a_field field"
> +
> + gdb_test "print $var%array_two" \
> + " = \\(\\(2, 2, 2\\) \\(2, 2, 2\\)\\)" \
> + "print array_two field"
> + }
> +}
> +
> +# Create new user variables for the fields of some_var, and show that
> +# modifying these variables does not change the original value from
> +# the program.
> +gdb_test_no_output "set \$b = some_var%array_one"
> +gdb_test_no_output "set \$c = some_var%array_two"
> +gdb_test "print \$b" \
> + " = \\(\\(1, 1\\) \\(1, 1\\) \\(1, 1\\)\\)"
> +gdb_test "print \$c" \
> + " = \\(\\(2, 2, 2\\) \\(2, 2, 2\\)\\)"
> +gdb_test_no_output "set \$b(2,2) = 3"
> +gdb_test_no_output "set \$c(3,1) = 4"
> +gdb_test "print \$b" \
> + " = \\(\\(1, 1\\) \\(1, 3\\) \\(1, 1\\)\\)" \
> + "print \$b after a change"
> +gdb_test "print \$c" \
> + " = \\(\\(2, 2, 4\\) \\(2, 2, 2\\)\\)" \
> + "print \$c after a change"
> +gdb_test "print some_var%array_one" \
> + " = \\(\\(1, 1\\) \\(1, 1\\) \\(1, 1\\)\\)"
> +gdb_test "print some_var%array_two" \
> + " = \\(\\(2, 2, 2\\) \\(2, 2, 2\\)\\)"
> +
> +# Now modify the user variable '$a', which is a copy of 'some_var',
> +# and then check how this change is reflected in the original
> +# 'some_var', and the user variable $a.
> +#
> +# When we change 'a_field' which is a non-dynamic field within the
> +# user variable, the change is only visible within the user variable.
> +#
> +# In contrast, when we change 'array_one' and 'array_two', which are
> +# both fields of dynanic type, then the change is visible in both the
> +# user variable and the original program variable 'some_var'. This
> +# makes sense if you consider the dynamic type as if it was a C
> +# pointer with automatic indirection.
> +gdb_test_no_output "set \$a%a_field = 3"
> +gdb_test_no_output "set \$a%array_one(2,2) = 3"
> +gdb_test_no_output "set \$a%array_two(3,1) = 4"
> +gdb_test "print \$a" \
> + " = \\( array_one = \\(\\(1, 1\\) \\(1, 3\\) \\(1, 1\\)\\), a_field = 3, array_two = \\(\\(2, 2, 4\\) \\(2, 2, 2\\)\\) \\)"
> +gdb_test "print some_var" \
> + " = \\( array_one = \\(\\(1, 1\\) \\(1, 3\\) \\(1, 1\\)\\), a_field = 5, array_two = \\(\\(2, 2, 4\\) \\(2, 2, 2\\)\\) \\)"
> diff --git a/gdb/testsuite/gdb.fortran/intvar-dynamic-types.f90 b/gdb/testsuite/gdb.fortran/intvar-dynamic-types.f90
> new file mode 100644
> index 00000000000..ace864812de
> --- /dev/null
> +++ b/gdb/testsuite/gdb.fortran/intvar-dynamic-types.f90
> @@ -0,0 +1,32 @@
> +! Copyright 2020 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 internal_var_test
> + type :: some_type
> + integer, allocatable :: array_one (:,:)
> + integer :: a_field
> + integer, allocatable :: array_two (:,:)
> + end type some_type
> +
> + type(some_type) :: some_var
> +
> + allocate (some_var%array_one (2,3))
> + allocate (some_var%array_two (3,2))
> + some_var%array_one (:,:) = 1
> + some_var%a_field = 5
> + some_var%array_two (:,:) = 2
> + deallocate (some_var%array_one) ! Break here.
> + deallocate (some_var%array_two)
> +end program internal_var_test
> diff --git a/gdb/value.c b/gdb/value.c
> index 7c36c31dccd..12b0fd7b48c 100644
> --- a/gdb/value.c
> +++ b/gdb/value.c
> @@ -1784,12 +1784,37 @@ set_value_component_location (struct value *component,
> component->location.computed.closure = funcs->copy_closure (whole);
> }
>
> - /* If type has a dynamic resolved location property
> - update it's value address. */
> + /* If either the WHOLE value, or the COMPONENT value has a dynamic
> + resolved location property then update the address of the COMPONENT.
> +
> + If the COMPONENT itself has a dynamic location, and was an
> + lval_internalvar_component, then we change this to lval_memory.
> + Usually a component of an internalvar is created non-lazy, and has its
> + content immediately copied from the parent internalvar. However,
> + for components with a dynamic location, the content of the component
> + is not contained within the parent, but is instead accessed
> + indirectly. Further, the component will be created as a lazy value.
> +
> + By changing the type of the component to lval_memory we ensure that
> + value_fetch_lazy can successfully load the component. */
> type = value_type (whole);
> if (NULL != TYPE_DATA_LOCATION (type)
> && TYPE_DATA_LOCATION_KIND (type) == PROP_CONST)
> set_value_address (component, TYPE_DATA_LOCATION_ADDR (type));
> +
> + type = value_type (component);
> + if (NULL != TYPE_DATA_LOCATION (type)
> + && TYPE_DATA_LOCATION_KIND (type) == PROP_CONST)
> + {
> + if (VALUE_LVAL (component) == lval_internalvar_component)
> + {
> + gdb_assert (value_lazy (component));
> + VALUE_LVAL (component) = lval_memory;
> + }
> + else
> + gdb_assert (VALUE_LVAL (component) == lval_memory);
> + set_value_address (component, TYPE_DATA_LOCATION_ADDR (type));
> + }
> }
>
> /* Access to the value history. */
> --
> 2.25.4
>
More information about the Gdb-patches
mailing list